douchi1945 2016-06-25 18:00
浏览 281
已采纳

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

I am trying to interface with some C code from Go. Using cgo, this has been relatively straight-forward until I hit this (fairly common) case: needing to pass a pointer to a structure that itself contains a pointer to some data. I cannot seem to figure out how to do this from Go without resorting to putting the creation of the structure into the C code itself, which I'd prefer not to do. Here is a snippet that illustrates the problem:

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)
}

While this compiles fine, trying to run it results in:

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

In my case, the data being passed to the C call doesn't persist past the call (i.e. the C code in question digs into the structure, copies what it needs, then returns).

  • 写回答

1条回答 默认 最新

  • doudui5753 2016-06-25 18:43
    关注

    See "Passing pointers" section in cgo docs:

    Go code may pass a Go pointer to C provided the Go memory to which it points does not contain any Go pointers.

    And also:

    These rules are checked dynamically at runtime. The checking is controlled by the cgocheck setting of the GODEBUG environment variable. The default setting is GODEBUG=cgocheck=1, which implements reasonably cheap dynamic checks. These checks may be disabled entirely using GODEBUG=cgocheck=0. Complete checking of pointer handling, at some cost in run time, is available via GODEBUG=cgocheck=2.

    If you run the snippet you've provided with:

    GODEBUG=cgocheck=0 go run snippet.go
    

    Then there is no panic. However, the correct way to go is to use C.malloc (or obtain a "C pointer" from somewhere else):

    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)
    }
    

    It works because while regular Go pointers are not allowed, C.malloc returns a "C pointer":

    Go pointer means a pointer to memory allocated by Go (such as by using the & operator or calling the predefined new function) and the term C pointer means a pointer to memory allocated by C (such as by a call to C.malloc). Whether a pointer is a Go pointer or a C pointer is a dynamic property determined by how the memory was allocated.

    Note that you need to include stdlib.h to use C.free.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥50 易语言把MYSQL数据库中的数据添加至组合框
  • ¥20 求数据集和代码#有偿答复
  • ¥15 关于下拉菜单选项关联的问题
  • ¥20 java-OJ-健康体检
  • ¥15 rs485的上拉下拉,不会对a-b<-200mv有影响吗,就是接受时,对判断逻辑0有影响吗
  • ¥15 使用phpstudy在云服务器上搭建个人网站
  • ¥15 应该如何判断含间隙的曲柄摇杆机构,轴与轴承是否发生了碰撞?
  • ¥15 vue3+express部署到nginx
  • ¥20 搭建pt1000三线制高精度测温电路
  • ¥15 使用Jdk8自带的算法,和Jdk11自带的加密结果会一样吗,不一样的话有什么解决方案,Jdk不能升级的情况