我发布这是一个问题/答案,因为我需要一段时间来解决问题,我不介意对我的解决方案提出一些反馈意见。在Go / CGo中,如何使用作为指针传递的C数组?
例如,使用此C结构:
struct _GNetSnmpVarBind {
guint32 *oid; /* name of the variable */
gsize oid_len; /* length of the name */
... and other fields
};
我想将oid字段转换为Go字符串,我将如何使用guint32 *指针?
答案 0 :(得分:4)
我这样做的方法是找到要读取的字节数(guint32 * oid_len的大小),然后对字节数做一个binary.Read(),然后通过大小的块来循环。回想起来容易;困难的部分是让类型转换工作,因为Go比C更严格。
例如,这里是将guint32 *转换为Go字符串(表示SNMP OID)的Go代码:
func gIntArrayOidString(oid *_Ctype_guint32, oid_len _Ctype_gsize) (result string) {
size := int(unsafe.Sizeof(*oid))
length := int(oid_len)
gbytes := C.GoBytes(unsafe.Pointer(oid), (_Ctype_int)(size*length))
buf := bytes.NewBuffer(gbytes)
for i := 0; i < length; i++ {
var out uint32
if err := binary.Read(buf, binary.LittleEndian, &out); err == nil {
result += fmt.Sprintf(".%d", out)
} else {
return "<error converting oid>"
}
}
if len(result) > 1 {
return result[1:] // strip leading dot
}
return "<error converting oid>"
}
评论
上下文:代码来自gsnmpgo。
答案 1 :(得分:4)
您可以使用a tip I saw in the go wiki
将C数组转换为Go切片未经测试但希望你明白了!不要让切片比C数据更长寿,因为它直接指向它。
上次我使用它时,我查看了反汇编,它生成了非常高效的代码。
func gIntArrayOidString(oid *_Ctype_guint32, oid_len _Ctype_gsize) (result string) {
var oids []uint32
sliceHeader := (*reflect.SliceHeader)((unsafe.Pointer(&oids)))
sliceHeader.Cap = oid_len
sliceHeader.Len = oid_len
sliceHeader.Data = uintptr(unsafe.Pointer(oid))
var result string
for _, value := range oids {
result += fmt.Sprintf(".%d", value)
}
return result[1:]
}
答案 2 :(得分:2)
我假设gsnmp中的值不一定是little-endian,而是本机字节顺序。我只想使用unsafe.Sizeof迭代数组。 e.g。
package main
import (
"unsafe"
"fmt"
)
var ints = [...]int32 {1, 2, 3}
func main() {
var result string
var p *int32 = &ints[0]
for i := 0; i < len(ints); i++ {
result += fmt.Sprintf(".%d", *p)
p = (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(p))+unsafe.Sizeof(*p)))
}
fmt.Println(result[1:])
}