如何在Go中填写void * C指针?

时间:2016-06-25 18:00:20

标签: go cgo

我正在尝试与Go中的一些C代码进行交互。使用cgo,这是相对直接的,直到我遇到这个(相当常见)的情况:需要将指针传递给一个结构,该结构本身包含指向某些数据的指针。我似乎无法弄清楚如何从Go中做到这一点,而不是将结构的创建放入C代码本身,我不想这样做。这是一个说明问题的片段:

package main

// typedef struct {
//     int   size;
//     void *data;
// } info;
//
// void test(info *infoPtr) {
//     // Do something here...
// }
import "C"

import "unsafe"

func main() {
    var data uint8 = 5
    info := &C.info{size: C.int(unsafe.Sizeof(data)), data: unsafe.Pointer(&data)}
    C.test(info)
}

虽然编译得很好,但尝试运行它会导致:

panic: runtime error: cgo argument has Go pointer to Go pointer

在我的情况下,传递给C调用的数据不会持续超过调用(即有问题的C代码会深入到结构中,复制它需要的内容,然后返回)。

1 个答案:

答案 0 :(得分:3)

请参阅cgo文档中的"Passing pointers"部分:

  

Go代码可以将Go指针传递给C,前提是它指向的Go内存不包含任何Go指针。

还有:

  

在运行时动态检查这些规则。检查由GODEBUG环境变量的cgocheck设置控制。默认设置是GODEBUG = cgocheck = 1,它实现了相当便宜的动态检查。可以使用GODEBUG = cgocheck = 0完全禁用这些检查。可以通过GODEBUG = cgocheck = 2以运行时的某些代价完成对指针处理的检查。

如果您运行了以下提供的代码段:

GODEBUG=cgocheck=0 go run snippet.go

然后没有恐慌。但是,正确的方法是使用C.malloc(或从其他地方获取" C指针")

package main

// #include <stdlib.h>
// typedef struct {
//     int   size;
//     void *data;
// } info;
//
// void test(info *infoPtr) {
//     // Do something here...
// }
import "C"

import "unsafe"

func main() {
    var data uint8 = 5

    cdata := C.malloc(C.size_t(unsafe.Sizeof(data)))
    *(*C.char)(cdata) = C.char(data)
    defer C.free(cdata)

    info := &C.info{size: C.int(unsafe.Sizeof(data)), data: cdata}
    C.test(info)
}

它的工作原理是因为虽然不允许使用常规Go指针,C.malloc会返回一个&#34; C指针&#34;:

  

Go指针指向由Go分配的内存的指针(例如通过使用&amp;运算符或调用预定义的新函数),术语C指针指向由C分配的内存的指针(例如通过调用C) .malloc)。指针是Go指针还是C指针是一个动态属性,由内存的分配方式决定。

请注意,您需要包含stdlib.h才能使用C.free