将 Go []byte 转换为 C *char

Convert Go []byte to a C *char

我有一个 byte.Buffer,我使用 binary.Write() 函数将数据打包。然后我需要将这个字节数组发送到 C 函数。使用 Go 1.6 我还没有成功解决这个问题。

buf := new(bytes.Buffer) //create my buffer
....
binary.Write(buf, binary.LittleEndian, data) //write my data to buffer here
addr := (*C.uchar)(unsafe.Pointer(&buf.Bytes()[0])) //convert buffers byte array to a C array
rc := C.the_function(addr, C.int(buf.Len())) //Fails here

它在调用 C 函数的行上失败说:

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

C函数:

int the_function(const void *data, int nbytes);

我能够使以下内容正常工作,但是将字节数组转换为字符串感觉不对。有一个更好的方法吗?此方法是否存在对数据产生副作用的风险?

addr := unsafe.Pointer(C.CString(string(buf.Bytes()[0]))

同样,这需要在引入了更严格的 cgo 指针规则的 Go 1.6 下工作。

谢谢。

如果你想使用你的第一种方法,你需要在函数调用参数之外创建切片,并避免临时分配的切片头或参数中的外部结构,所以cgo检查不' 将其视为存储在 Go 中的指针。

b := buf.Bytes()
rc := C.the_function(unsafe.Pointer(&b[0]), C.int(buf.Len()))

C.CString 方法会更安全,因为数据被复制到 C 缓冲区中,所以没有指向 Go 内存的指针,bytes.Buffer 后面的切片不可能被修改或超出范围。您需要转换整个字符串,而不仅仅是第一个字节。此方法确实需要分配和复制两次,但是如果数据量很小,与 cgo 调用本身的开销相比,这可能不是问题。

str := buf.String()
p := unsafe.Pointer(C.CString(str))
defer C.free(p)
rc = C.the_function(p, C.int(len(str)))

如果该解决方案不接受数据的 2 个副本,则还有第三种选择,您自己 malloc C 缓冲区,然后将单个副本复制到该缓冲区中:

p := C.malloc(C.size_t(len(b)))
defer C.free(p)

// copy the data into the buffer, by converting it to a Go array
cBuf := (*[1 << 30]byte)(p)
copy(cBuf[:], b)
rc = C.the_function(p, C.int(buf.Len()))

但是对于后两个选项,不要忘记释放 malloc 的指针。

您的程序崩溃,因为在 go1.6 中更改了将指针传递到 C 的规则(有关详细信息,请参阅 https://tip.golang.org/doc/go1.6#cgo)。

我不知道你的程序为什么会崩溃,所以我创建了 Go 问题 https://github.com/golang/go/issues/14546

但是不管这个问题的答案如何,我都不会使用 bytes.Buffer 的内部位(就像你那样)直接传递给 cgo。 bytes.Buffer 实现将来可能会发生变化,您的程序将开始神秘地崩溃。我只是将您需要的数据复制到任何合适的结构中,然后使用它传递给 cgo。