Go 指针存储到非 Go 内存中

Go pointer stored into non-Go memory

我有一个新手 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

我知道数组的内存是在 C 语言中使用 malloc 分配的。我试图在将 C Box 传递给 C 函数之前将其添加到数组中。解决这个问题的方法是让我在 C 中编写一个函数,该函数可以接收数组和创建结构所需的项目,而我在 C 中执行该部分吗?我正在尝试最大程度地减少对 C 的调用次数,因此如果我可以从 Go 创建数组那就太好了。真的在为如何安全地将数据数组传递给 C 中的函数而苦苦挣扎。

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

的指针

I'm trying to add C Box to the array before passing it to a C function.

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

Would the fix for this be for me to write a function in C which can receive the array and the items needed to create the struct and I do that part in C instead?

虽然您可以尝试这样做,但似乎...不是最理想的。

I'm trying to minimise the number of calls to C so If I can create the array from Go that would be great. Really struggling with how to pass an array of data through to a function in C safely.

C 从来都不是真正的 安全 但我怀疑你最好的选择是利用 C 实现“数组”的方式,这是一种穷人的切片. (或者,也许更准确地说,一个 Go 切片是一个正确完成的“C 数组”。)请记住,一个切片在 Go 中被实现为一个三元素头:

  • 指向存储区基址的指针;
  • 当前存储区长度;和
  • 存储区容量

然后可以容纳一些 运行 时间计算的某种类型的项目数 n。 C 的动态“数组”以相同的方式工作,只是没有适当的切片头的安全性。

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

void f(T *base, size_t n);

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

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

如果您无法重写 C 代码,则至少需要两次 C.malloc 调用。但是,您可以再次将所有 struct Box 分配到一个 C.malloc 中,并使它们都相邻——即 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)