从 Raspberry PI 上的 GO 程序我试图调用一个函数(转换为 C 函数的 Matlab 函数)并且该函数的输入是一个指向结构的指针,该结构包含指向 double (数据)的指针和一个指向 int(size) 和两个 int(allocatedSize, numDimensions) 的指针。我尝试了几种方法但没有任何效果,当我通过编译时它通常会抛出一个 panic :运行时错误:当我运行程序时,cgo 参数有指向 Go 指针的 Go 指针。
sumArray.c
/*sumArray.C*/
/* Include files */
#include "sumArray.h"
/* Function Definitions */
double sumArray(const emxArray_real_T *A1)
{
double S1;
int vlen;
int k;
vlen = A1->size[0];
if (A1->size[0] == 0) {
S1 = 0.0;
} else {
S1 = A1->data[0];
for (k = 2; k <= vlen; k++) {
S1 += A1->data[k - 1];
}
}
return S1;
}
sumArray.h
#ifndef SUMARRAY_H
#define SUMARRAY_H
/* Include files */
#include <stddef.h>
#include <stdlib.h>
#include "sumArray_types.h"
/* Function Declarations */
extern double sumArray(const emxArray_real_T *A1);
#endif
sumArray_types.h
#ifndef SUMARRAY_TYPES_H
#define SUMARRAY_TYPES_H
/* Include files */
/* Type Definitions */
#ifndef struct_emxArray_real_T
#define struct_emxArray_real_T
struct emxArray_real_T
{
double *data;
int *size;
int allocatedSize;
int numDimensions;
};
#endif /*struct_emxArray_real_T*/
#ifndef typedef_emxArray_real_T
#define typedef_emxArray_real_T
typedef struct emxArray_real_T emxArray_real_T;
#endif /*typedef_emxArray_real_T*/
#endif
/* End of code generation (sumArray_types.h) */
主.go
// #cgo CFLAGS: -g -Wall
// #include <stdlib.h>
// #include "sumArray.h"
import "C"
import (
"fmt"
)
func main() {
a1 := [4]C.Double{1,1,1,1}
a2 := [1]C.int{4}
cstruct := C.emxArray_real_T{data: &a1[0], size: &a2[0]}
cstructArr := [1]C.emxArray_real_T{cstruct}
y := C.sumArray(&cstructArr[0])
fmt.Print(float64(y))
}
在这个例子中,当我运行程序时,我得到了 panic: runtime error: cgo argument has Go pointer to Go pointer。
我不知道如何让它工作,或者是否有可能让它工作。我希望有人能帮助我或就如何解决这个问题给出一些指导。
最佳答案
评论太多了,所以这里是答案。
一、原文:
一个直接的解决方案是使用 C.malloc(4 * C.sizeof(C.double))
来分配 double
的数组。请注意,您必须确保在完成后对其调用 C.free()
。这同样适用于单个 int
的第二个数组。
现在,您对 Mattanis 的评论进行了一些重新格式化:
thanks for giving some pointers. I tried with
a1 := [4]C.double{1,1,1,1} sizeA1 := C.malloc(4 * C.sizeof_double) cstruct := C.emxArray_real_T{ data: &a1[0], size: (*C.int)(sizeA1) } y := C.sumArray(cstruct) defer C.free(sizeA1)
but it gave me the same answer as before cgo argument has Go pointer to Go pointer when I tried to run the program
你好像还是没捕获重点。当您使用 cgo
时,有两个不相交“内存 View ”:
“Go 内存”是 Go 运行时分配的一切,为您正在运行的进程提供动力——代表该进程。此内存(大多数情况下,除非有奇怪的技巧)为 GC 所知——它是运行时的一部分。
“C 内存”是由 C 代码分配的内存——通常通过调用
libc
的malloc()
/realloc()
.
现在想象一个不太牵强的场景:
- 你的程序运行,C“端”被初始化并且 生成自己的线程(或多个线程),并持有它们的句柄。
- 您的 Go“端”已经使用多个线程来运行您的 goroutine。
- 你在你的 Go 代码中分配一些 Go 内存并传递它 到C端。
- C 端将该内存的地址传递给它自己的一个或多个线程。
- 您的程序继续运行,C 端线程也是如此——与您的 Go 代码并行运行。
因此,您有一个相当经典的场景,在该场景中,您会遇到非同步并行内存访问的 super 简单情况,这肯定是当今多核多插槽硬件上灾难的收容所。
还要考虑到 Go 是一种比 C 更高级的编程语言;至少,它具有自动垃圾收集功能,请注意 the Go spec 中没有任何内容指定必须如何准确地指定 GC。 这意味着,Go 的特定实现(包括 future 的引用实现)可以自由地允许其 GC 移动内存中的任意对象¹,这意味着更新指向内存块的每个指针在其原始位置指向 block 的新位置中的相同位置 - 在它被移动之后。
考虑到这些因素,Go 开发者假设,为了保持 cgo
-using programs future-proof²,禁止向 C 传递任何包含指向其他 Go 内存的指针的内存块 block 。
不过,传递包含指向 C 内存的指针的 Go 内存块是可以的。
回到您第二条评论中的示例,
你仍然在 Go 内存中分配 4 个 double
的数组,a1
。
然后语句 cstruct := C.emxArray_real_T{...}
再次在 Go 内存中分配 C.emxArray_real_T
的实例,因此在您初始化它之后data
字段带有指向 Go 内存的指针(&a1[0]
),然后将其地址传递给 C 端,运行时在实际调用之前执行动态检查C 端并使您的程序崩溃。
¹ 例如,这是所谓的“世代”垃圾收集器的典型行为。
² 也就是说,您使用相同“主要”版本的 Go 编译器的 future 版本重新编译您的程序,并且该程序继续运行,无需修改。
关于go - 如何调用采用包含指针的 c 结构的 c 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56363771/