当CMatrix转换为切片时,Go-vet抱怨unsafeptr

3gtaxfhh  于 2023-09-28  发布在  Go
关注(0)|答案(2)|浏览(97)

我在Go中使用openCV,并具有以下功能:

func ConvertMatToSlice(c_Mat *CMatrix) []float32 {
    c_output := C.Convert(c_Mat.CType)
    h := &reflect.SliceHeader{
        Data: uintptr(unsafe.Pointer(c_output.val)),
        Len:  int(c_output.length),
        Cap:  int(c_output.length),
    }
    return *(*[]float32)(unsafe.Pointer(h))
}

当我运行go vet ./...时,它会给我:

opencv_wrapper.go:31:7: unsafeptr: possible misuse of reflect.SliceHeader (govet)
    h := &reflect.SliceHeader{
         ^

有没有其他的方法来获得切片,因为我相信去兽医有它的理由抱怨这一点。

ua4mk5z4

ua4mk5z41#

请参见Go Issue #40701。更安全的替代方案如下:

func ConvertMatToSlice(c_Mat *CMatrix) []float32 {
    c_output := C.Convert(c_Mat.CType)
    result := make([]float32, c_output.length)
    sh := (*reflect.SliceHeader)(unsafe.Pointer(&result))
    sh.Data = uintptr(unsafe.Pointer(c_output.val))
    return result
}

我做了一个test program模拟你的代码行为,而不使用cgo。因此,让我们将其放在一个目录中,并使用go build -gcflags "-m -m"进行一些转义分析。
我们感兴趣的是确保我们返回的切片(切片头)在堆上分配,它在Data字段中指向的指针也是如此。正如我们从逃逸分析中看到的,b逃逸,切片分配也逃逸:

./main.go:10:6: b escapes to heap:
./main.go:10:6:   flow: {storage for &Output literal} = &b:
./main.go:10:6:     from &b (address-of) at ./main.go:17:38
[...]
./main.go:25:16: make([]float32, c_output.length) escapes to heap:
./main.go:25:16:   flow: {heap} = &{storage for make([]float32, c_output.length)}:
./main.go:25:16:     from make([]float32, c_output.length) (non-constant size) at ./main.go:25:16

在您的例子中,您必须从C代码中确保指针在堆上分配。
一般来说,从我提到的Go问题的讨论中可以得出的规则是永远不要自己创建SliceHeaders。它们可能会起作用,但它们会导致内存损坏,在代码中造成困难和难以调试的情况。如果您确实需要自己访问切片,可以使用makereflect.MakeSlice,它们可以保证正常工作,并与GC正确协作,然后对切片头进行必要的更改。

mwngjboj

mwngjboj2#

在Go 1.21中,refelect.SliceHeader头的使用已经被删除。执行类似操作的替代代码现在如下所示

func ConvertMatToSlice(c_Mat *CMatrix) []float32 {
    c_output := C.Convert(c_Mat.CType)
    sh := unsafe.Slice(c_output.val, c_output.length)
    return *(*[]float32)(unsafe.Pointer(&sh))
}

前面的答案中所说的一切仍然有效。

相关问题