Go/CGo - 如何使用作为指针传递的C数组

9
我将以问题/答案的形式发布这篇文章,因为我花了一些时间来解决它,我不介意对我的解决方案进行反馈。在 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 *指针?

3个回答

8
你可以使用 Go wiki中提到的技巧将C数组转换为Go切片。
未经测试,但希望你能理解!不要让切片比C数据存活时间更长,因为它直接指向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:]
}

这正是我正在寻找的! - Siu Ching Pong -Asuka Kenji-
2
截至2015年,这已从Go Wiki中删除,因为“它不能安全或可移植地使用”。 - psanford

4
我所采用的方法是找到需要读取的字节数(guint32 * oid_len 的大小),然后对这些字节进行二进制读取(binary.Read()),接着按照指定大小循环处理这些字节。回想起来还是挺简单的,但让Go语言的类型转换正常工作却是一件困难的事情,因为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


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:])
}

1
谢谢Axw,我会去看看的。 - Sonia Hamilton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接