go - 如何调用采用包含指针的 c 结构的 c 函数

标签 go cgo

从 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 代码分配的内存——通常通过调用 libcmalloc()/realloc() .

现在想象一个不太牵强的场景:

  1. 你的程序运行,C“端”被初始化并且 生成自己的线程(或多个线程),并持有它们的句柄。
  2. 您的 Go“端”已经使用多个线程来运行您的 goroutine。
  3. 你在你的 Go 代码中分配一些 Go 内存并传递它 到C端。
  4. C 端将该内存的地址传递给它自己的一个或多个线程。
  5. 您的程序继续运行,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/

相关文章:

eclipse - goClipse:执行准备启动时发生内部错误。原因:java.lang.NullPointerException 为什么?

python - 无法访问显示组件 - 从 Windows 服务调用

go - 如何为ARM体系结构构建SO库

C 常量上的 Cgo 链接器错误

c - 如何将 uint8_t 数组从 C 发送到 GO

go - 创建一个包含 map 的 slice

javascript - 在js中设置cookie,无法在golang中读取

c - 从go代码调用时对c函数的 undefined reference

go - 读取 http 响应时内存使用量增加

go - 使用 calloc() 与 make() 进行 slice 时,程序会更改内存值