Go言語とCgo: intポインタの扱い

By quonta 4月 8, 2024

GoとCの間のポインタ渡し

Go言語とC言語の間でポインタを渡す際には、cgoパッケージが提供する機能を利用します。具体的には、GoのポインタをCの関数に渡すことが可能です。

package main

/*
#include <stdio.h>

void printNum(int *num) {
    printf("%d\n", *num);
}
*/
import "C"
import "unsafe"

func main() {
    var num int = 10
    C.printNum((*C.int)(unsafe.Pointer(&num)))
}

上記のコードでは、Goの変数numのアドレスをunsafe.Pointerを通じてCのintポインタに変換し、Cの関数printNumに渡しています。

ただし、この方法には注意点があります。GoのガベージコレクタはCのポインタを認識できないため、Cの関数がGoのポインタを保持している間にGoのガベージコレクタによってメモリが解放されてしまう可能性があります。そのため、GoのポインタをCに渡す際には、そのポインタが有効であることを保証する必要があります。これには、runtime.KeepAlive関数を使用します。

package main

/*
#include <stdio.h>

void printNum(int *num) {
    printf("%d\n", *num);
}
*/
import "C"
import (
    "runtime"
    "unsafe"
)

func main() {
    num := 10
    C.printNum((*C.int)(unsafe.Pointer(&num)))
    runtime.KeepAlive(num)
}

上記のコードでは、runtime.KeepAlive(num)を呼び出すことで、numがガベージコレクタによって回収されないようにしています。これにより、printNum関数がnumを安全に参照できることが保証されます。このように、GoとCの間でポインタを渡す際には、メモリ管理に注意が必要です。

GoのポインタをCで扱う場合

GoのポインタをCで扱う場合も、cgoパッケージが提供する機能を利用します。具体的には、Cの関数にGoのポインタを渡し、そのポインタを通じてGoのメモリを直接操作することが可能です。

package main

/*
#include <stdio.h>

void addNum(int *num) {
    (*num)++;
}
*/
import "C"
import "unsafe"

func main() {
    var num int = 10
    C.addNum((*C.int)(unsafe.Pointer(&num)))
    println(num)  // 11
}

上記のコードでは、Goの変数numのアドレスをunsafe.Pointerを通じてCのintポインタに変換し、Cの関数addNumに渡しています。addNum関数は、渡されたポインタが指す値をインクリメントします。その結果、Goの変数numの値が変更されます。

ただし、この方法には注意点があります。GoのガベージコレクタはCのポインタを認識できないため、Cの関数がGoのポインタを保持している間にGoのガベージコレクタによってメモリが解放されてしまう可能性があります。そのため、GoのポインタをCに渡す際には、そのポインタが有効であることを保証する必要があります。これには、runtime.KeepAlive関数を使用します。

package main

/*
#include <stdio.h>

void addNum(int *num) {
    (*num)++;
}
*/
import "C"
import (
    "runtime"
    "unsafe"
)

func main() {
    num := 10
    C.addNum((*C.int)(unsafe.Pointer(&num)))
    runtime.KeepAlive(num)
    println(num)  // 11
}

上記のコードでは、runtime.KeepAlive(num)を呼び出すことで、numがガベージコレクタによって回収されないようにしています。これにより、addNum関数がnumを安全に参照できることが保証されます。このように、GoとCの間でポインタを渡す際には、メモリ管理に注意が必要です。このテーマについては、次の小見出しで詳しく説明します。

unsafe.Pointerによるポインタの渡し

Go言語では、unsafe.Pointerを使用して任意の型のポインタを別の型のポインタに変換することができます。これにより、GoのポインタをCの関数に渡すことが可能になります。

package main

/*
#include <stdio.h>

void printFloat(float *num) {
    printf("%f\n", *num);
}
*/
import "C"
import "unsafe"

func main() {
    var num float32 = 1.23
    C.printFloat((*C.float)(unsafe.Pointer(&num)))
}

上記のコードでは、Goの変数numのアドレスをunsafe.Pointerを通じてCのfloatポインタに変換し、Cの関数printFloatに渡しています。

ただし、unsafe.Pointerを使用する際には注意が必要です。unsafe.Pointerはその名の通り、安全ではない操作を可能にするためのものです。そのため、unsafe.Pointerを使用することで、型安全性を破る可能性があります。また、GoのガベージコレクタはCのポインタを認識できないため、Cの関数がGoのポインタを保持している間にGoのガベージコレクタによってメモリが解放されてしまう可能性があります。

そのため、unsafe.Pointerを使用する際には、そのポインタが有効であることを保証する必要があります。これには、runtime.KeepAlive関数を使用します。

package main

/*
#include <stdio.h>

void printFloat(float *num) {
    printf("%f\n", *num);
}
*/
import "C"
import (
    "runtime"
    "unsafe"
)

func main() {
    num := float32(1.23)
    C.printFloat((*C.float)(unsafe.Pointer(&num)))
    runtime.KeepAlive(num)
}

上記のコードでは、runtime.KeepAlive(num)を呼び出すことで、numがガベージコレクタによって回収されないようにしています。これにより、printFloat関数がnumを安全に参照できることが保証されます。このように、unsafe.Pointerを使用する際には、メモリ管理に注意が必要です。このテーマについては、次の小見出しで詳しく説明します。

uintptrを用いたポインタの渡し

Go言語では、uintptr型を使用して任意の型のポインタを整数値に変換することができます。これにより、GoのポインタをCの関数に渡すことが可能になります。

package main

/*
#include <stdio.h>

void printAddr(uintptr_t addr) {
    printf("%p\n", (void *)addr);
}
*/
import "C"
import "unsafe"

func main() {
    var num int = 10
    C.printAddr((C.uintptr_t)(unsafe.Pointer(&num)))
}

上記のコードでは、Goの変数numのアドレスをunsafe.Pointerを通じてuintptrに変換し、Cの関数printAddrに渡しています。

ただし、uintptrを使用する際には注意が必要です。uintptrはその名の通り、ポインタの値を整数値として扱うためのものです。そのため、uintptrを使用することで、型安全性を破る可能性があります。また、GoのガベージコレクタはCのポインタを認識できないため、Cの関数がGoのポインタを保持している間にGoのガベージコレクタによってメモリが解放されてしまう可能性があります。

そのため、uintptrを使用する際には、そのポインタが有効であることを保証する必要があります。これには、runtime.KeepAlive関数を使用します。

package main

/*
#include <stdio.h>

void printAddr(uintptr_t addr) {
    printf("%p\n", (void *)addr);
}
*/
import "C"
import (
    "runtime"
    "unsafe"
)

func main() {
    num := 10
    C.printAddr((C.uintptr_t)(unsafe.Pointer(&num)))
    runtime.KeepAlive(num)
}

上記のコードでは、runtime.KeepAlive(num)を呼び出すことで、numがガベージコレクタによって回収されないようにしています。これにより、printAddr関数がnumを安全に参照できることが保証されます。このように、uintptrを使用する際には、メモリ管理に注意が必要です。このテーマについては、次の小見出しで詳しく説明します。

Goのバイト列をCで扱う

Go言語では、[]byte型を使用してバイト列を表現します。このバイト列をCの関数に渡すことも可能です。

package main

/*
#include <stdio.h>

void printBytes(char* bytes, int len) {
    for (int i = 0; i < len; i++) {
        printf("%02x ", (unsigned char)bytes[i]);
    }
    printf("\n");
}
*/
import "C"
import "unsafe"

func main() {
    bytes := []byte{0x01, 0x02, 0x03, 0x04, 0x05}
    C.printBytes((*C.char)(unsafe.Pointer(&bytes[0])), C.int(len(bytes)))
}

上記のコードでは、Goのバイト列bytesの先頭要素のアドレスをunsafe.Pointerを通じてCのcharポインタに変換し、Cの関数printBytesに渡しています。printBytes関数は、渡されたポインタが指すバイト列を16進数で出力します。

ただし、この方法には注意点があります。GoのガベージコレクタはCのポインタを認識できないため、Cの関数がGoのポインタを保持している間にGoのガベージコレクタによってメモリが解放されてしまう可能性があります。そのため、GoのポインタをCに渡す際には、そのポインタが有効であることを保証する必要があります。これには、runtime.KeepAlive関数を使用します。

package main

/*
#include <stdio.h>

void printBytes(char* bytes, int len) {
    for (int i = 0; i < len; i++) {
        printf("%02x ", (unsigned char)bytes[i]);
    }
    printf("\n");
}
*/
import "C"
import (
    "runtime"
    "unsafe"
)

func main() {
    bytes := []byte{0x01, 0x02, 0x03, 0x04, 0x05}
    C.printBytes((*C.char)(unsafe.Pointer(&bytes[0])), C.int(len(bytes)))
    runtime.KeepAlive(bytes)
}

上記のコードでは、runtime.KeepAlive(bytes)を呼び出すことで、bytesがガベージコレクタによって回収されないようにしています。これにより、printBytes関数がbytesを安全に参照できることが保証されます。このように、GoとCの間でポインタを渡す際には、メモリ管理に注意が必要です。このテーマについては、次の小見出しで詳しく説明します。

CのポインタをGoで扱う

C言語から返されたポインタをGoで扱う場合も、cgoパッケージが提供する機能を利用します。具体的には、Cの関数から返されたポインタをGoのポインタに変換し、そのポインタを通じてCのメモリを直接操作することが可能です。

package main

/*
#include <stdlib.h>

int* createNum() {
    int* num = (int*)malloc(sizeof(int));
    *num = 10;
    return num;
}
*/
import "C"
import "unsafe"

func main() {
    numPtr := C.createNum()
    num := *(*int)(unsafe.Pointer(numPtr))
    println(num)  // 10
    C.free(unsafe.Pointer(numPtr))
}

上記のコードでは、Cの関数createNumから返されたポインタをunsafe.Pointerを通じてGoのintポインタに変換し、そのポインタが指す値を取得しています。

ただし、この方法には注意点があります。Cの関数から返されたポインタは、Cのメモリ空間を指しています。そのため、そのポインタを使用し終えたら、必ずfree関数を呼び出してメモリを解放する必要があります。これを怠ると、メモリリークが発生する可能性があります。

また、Cの関数から返されたポインタをGoで扱う際には、そのポインタが有効であることを保証する必要があります。これには、runtime.KeepAlive関数を使用します。

package main

/*
#include <stdlib.h>

int* createNum() {
    int* num = (int*)malloc(sizeof(int));
    *num = 10;
    return num;
}
*/
import "C"
import (
    "runtime"
    "unsafe"
)

func main() {
    numPtr := C.createNum()
    num := *(*int)(unsafe.Pointer(numPtr))
    println(num)  // 10
    runtime.KeepAlive(numPtr)
    C.free(unsafe.Pointer(numPtr))
}

上記のコードでは、runtime.KeepAlive(numPtr)を呼び出すことで、numPtrがガベージコレクタによって回収されないようにしています。これにより、numPtrを安全に参照できることが保証されます。このように、CのポインタをGoで扱う際には、メモリ管理に注意が必要です。

By quonta

Related Post

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です