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.

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

报告相同问题?

悬赏问题

  • ¥15 ansys fluent计算闪退
  • ¥15 有关wireshark抓包的问题
  • ¥15 需要写计算过程,不要写代码,求解答,数据都在图上
  • ¥15 向数据表用newid方式插入GUID问题
  • ¥15 multisim电路设计
  • ¥20 用keil,写代码解决两个问题,用库函数
  • ¥50 ID中开关量采样信号通道、以及程序流程的设计
  • ¥15 U-Mamba/nnunetv2固定随机数种子
  • ¥15 vba使用jmail发送邮件正文里面怎么加图片
  • ¥15 vb6.0如何向数据库中添加自动生成的字段数据。