douchi1945
2016-06-25 18:00 阅读 214
已采纳

如何在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 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.

    点赞 评论 复制链接分享

相关推荐