英文:
Go/CGo - how do you use a C array passed as a pointer
问题
我将这个问题/答案发布出来,因为我花了一些时间来解决它,我不介意对我的解决方案提供一些反馈。在Go/CGo中,如何处理作为指针传递的C数组?
例如,对于这个C结构体:
struct _GNetSnmpVarBind {
guint32 *oid; /* 变量的名称 */
gsize oid_len; /* 名称的长度 */
... 和其他字段
};
我想将oid字段转换为Go字符串,我应该如何处理guint32*指针?
英文:
I'm posting this as a question/answer, as it took me a while to work out, and I wouldn't mind some feedback on my solution. In Go/CGo, how do you work with a C array passed as a pointer?
For example, with this C struct:
struct _GNetSnmpVarBind {
guint32 *oid; /* name of the variable */
gsize oid_len; /* length of the name */
... and other fields
};
I want to convert oid field to a Go string, how would I work with the guint32* pointer?
答案1
得分: 8
你可以使用我在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:]
}
英文:
You could convert the C array into a Go slice using a tip I saw in the go wiki
Untested but hopefully you get the idea! Don't let the slice live longer than the C data though as it points directly into it.
Last time I used this I had a look at the disassembly and it generates very efficient code.
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
得分: 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:] // 去掉开头的点
}
return "<error converting oid>"
}
有什么意见吗?
英文:
The way I did it was to find the number of bytes to be read (size of a guint32 * oid_len), then did a binary.Read() on the number of bytes, then looped through be bytes in chunks of size. Easy in retrospect; the hard part was getting the type conversions working as Go is stricter than C.
For example here's the Go code for converting the guint32* to a Go string (representing an SNMP OID):
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>"
}
Comments?
Context: the code is from gsnmpgo.
答案3
得分: 2
我假设gsnmp中的值不一定是小端字节顺序,而是本机字节顺序。我只需使用unsafe.Sizeof来遍历数组。例如。
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:])
}
英文:
I assume the values from gsnmp are not necessarily in little-endian, but the native byte order. I would just use unsafe.Sizeof to iterate through the array. e.g.
<!-- language: go -->
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:])
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论