cGo指针存储在非Go内存中



我有一个新手CGO问题,想知道是否有人能帮我。当GODEBUG设置为cgocheck=2运行时,我的应用程序崩溃,出现以下

write of Go pointer 0xc0003b72c0 to non-Go memory 0x7fefa0016810
fatal error: Go pointer stored into non-Go memory

导致问题的代码是

cArray := C.malloc(C.size_t(len(fd.Faces)) * C.size_t(unsafe.Sizeof(uintptr(0))))
defer C.free(unsafe.Pointer(cArray))
a := (*[1<<30 - 1]*C.struct_Box)(cArray)
for index, value := range fd.GetFaceRectangles() {
box := &C.struct_Box{
left: C.int(value.Min.X),
top: C.int(value.Min.Y),
right: C.int(value.Max.X),
bottom: C.int(value.Max.Y),
}
a[index] = box
}
cBoxCount := C.int(len(fd.Faces))
ret := C.facerec_compute_multi(rec.ptr, cImgData, cLen,  &a[0], cBoxCount)

特别是这一行:

a[index] = box

我知道数组的内存是用malloc在C中分配的。在将C Box传递给C函数之前,我正在尝试将其添加到数组中。解决这个问题的方法是让我用C编写一个函数,它可以接收数组和创建结构所需的项,而我用C来完成这一部分吗?我正在尽量减少对C的调用次数,所以如果我能从Go创建数组,那就太好了。真的很难理解如何安全地将一组数据传递给C中的函数。

您已经分配了C内存来容纳许多单独的指针。每个单独的指针都可以设置为一个单独的值。就目前而言,这很好,但正如您所指出的,a[index] = box是一个问题,因为box本身持有指向Go内存C.struct_Box的指针。

我试图在将C Box传递给C函数之前将其添加到数组中。

要做到这一点,您需要一个C.malloc调用。正如所写的,每个C.struct_Box实例需要一个。

解决这个问题的方法是让我在C中写一个函数,它可以接收数组和创建结构所需的项,而我在C里完成这一部分吗?

虽然你可以尝试一下,但似乎。。。次优。

我正在尽量减少对C的调用次数,所以如果我能从Go创建数组,那就太好了。真的很难理解如何安全地将一组数据传递给C中的函数。

C从来都不是真正的安全😀但我怀疑你在这里最好的选择是利用C实现";阵列";,这就像是穷人的切片。(或者,也许更准确地说,Go切片是一个"C数组"。(记住,切片在Go中是作为三个元素的头实现的:

  • 指向存储区域底部的指针
  • 存储区域内的当前长度;以及
  • 存储区容量

然后它可以保存某种类型的项的一些运行时计算数n。C的动态";阵列";以同样的方式工作,只是没有合适的切片标头的安全性。

这意味着你可以编写你的C代码——提供你对这个C代码有的控制权——来获取一个指向存储区域底部的指针,以及一个intsize_t值,该值计算该区域中n项的数量。这样一个函数的签名是:

void f(T *base, size_t n);

其中CCD_ 8是项目的数量。(如果合适的话,添加第二个size_t,如果你也想提供一个容量,尽管如果内存有容量,并且函数将其修改为包含不同数量的项目,那么函数必须返回新数量的项目——即f不会是void——或者n本身必须通过引用或类似方式提供。(

在这种情况下,如果您可以安排C代码将指针指向nstruct Box-es中的第一个(而不是指向struct Box-es的n指针中的第一个中(,则可以在单个C.malloc调用中分配该指针。

如果不能重新编写C代码,那么至少需要两个C.malloc调用。然而,您可以再次在一个C.malloc中分配所有的struct Boxes,并使它们都相邻——即,一个C〃;数组"/可怜的人的切片——然后使每个指针指向下一个struct Box:

cArray := C.malloc(C.size_t(len(fd.Faces)) * C.size_t(unsafe.Sizeof(uintptr(0))))
defer C.free(unsafe.Pointer(cArray))
cBoxes := C.malloc(C.size_t(len(fd.Faces)) * C.size_t(unsafe.Sizeof(C.struct_Box)))
defer C.free(unsafe.Pointer(cBoxes))
a := (*[1<<30 - 1]*C.struct_Box)(cArray)
b := (*[1<<30 - 1]C.struct_Box)(cBoxes)
for index, value := range fd.GetFaceRectangles() {
b[index] = C.struct_Box{
left: C.int(value.Min.X),
top: C.int(value.Min.Y),
right: C.int(value.Max.X),
bottom: C.int(value.Max.Y),
}
a[index] = &b[index]
}
cBoxCount := C.int(len(fd.Faces))
ret := C.facerec_compute_multi(rec.ptr, cImgData, cLen,  &a[0], cBoxCount)

(我试图在这里保持代码结构尽可能相似,但这是未经测试的,因为我没有一些部分。(

旁白:您有len(fd.Faces)fd.GetFaceRectangles(),这里的代码假设for ... range fd.GetFaceRectangles()的迭代次数总是len(fd.Faces)。我之所以这么说,是因为我做出了同样的假设,除了你的例子之外,没有任何其他支持。

在Slack频道的一些帮助下,我最终完成了这项工作,只是直接添加了结构而不是指针。

// Create an array in C
arr := C.malloc(C.size_t(len(fd.Faces)) * C.sizeof_Box)
defer C.free(unsafe.Pointer(arr))
var boxes []C.struct_Box
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&boxes))
hdr.Data, hdr.Cap, hdr.Len = uintptr(arr), len(fd.Faces), len(fd.Faces)
for i, v := range fd.GetFaceRectangles() {
boxes[i] = C.struct_Box{
left: C.int(v.Min.X),
top: C.int(v.Min.Y),
right: C.int(v.Max.X),
bottom: C.int(v.Max.Y),
}
}
cBoxCount := C.int(len(fd.Faces))
ret := C.facerec_compute_multi(rec.ptr, cImgData, cLen, &(boxes[0]), cBoxCount)

最新更新