【问题标题】:Go/CGo - how do you use a C array passed as a pointerGo/CGo - 你如何使用作为指针传递的 C 数组
【发布时间】:2019-03-28 08:08:47
【问题描述】:

我将此作为问题/答案发布,因为我花了一段时间才解决问题,而且我不介意对我的解决方案提供一些反馈。在 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* 指针?

【问题讨论】:

    标签: go cgo


    【解决方案1】:

    您可以使用 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:]
    }
    

    【讨论】:

    • 这正是我要找的!
    • 自 2015 年起,这已从 go wiki 中删除,因为“它不能安全或便携地使用”。
    【解决方案2】:

    我这样做的方法是找到要读取的字节数(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

    【讨论】:

      【解决方案3】:

      我假设来自 gsnmp 的值不一定是 little-endian,而是原生字节顺序。我只会使用 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:])
      }
      

      【讨论】:

      • 谢谢Axw,我去看看。
      猜你喜欢
      • 2018-12-31
      • 1970-01-01
      • 2016-09-06
      • 2016-06-25
      • 2016-07-12
      • 2021-12-06
      • 1970-01-01
      • 1970-01-01
      • 2012-11-05
      相关资源
      最近更新 更多