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

时间:2019-05-29 15:15:28

标签: go cgo

从Raspberry PI上的GO程序中,我正在尝试调用一个函数(将Matlab函数转换为C函数),并且该函数的输入是指向结构的指针,而该结构包含指向double(data)的指针以及一个指向int(size)和两个int(allocatedSize,numDimensions)的指针。我尝试了几种方法,但是没有任何效果,当我通过编译后,通常会出现恐慌:运行时错误:运行程序时,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) */

main.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))
}

在此示例中,我感到恐慌:运行时错误:运行程序时,cgo参数具有指向Go指针的Go指针。

我不怎么使它工作或是否有可能使它工作。我希望有人能帮助我或就如何解决这个问题提供指导。

1 个答案:

答案 0 :(得分:0)

太多评论了,所以这里就是答案。

首先,原始文本:

一个直接的解决方案是使用C.malloc(4 * C.sizeof(C.double))分配double-s的数组。请注意,完成后必须确保对其调用C.free()。单个int的第二个数组也是如此。

现在,您对Mattanis的评论进行了重新格式化:

  

感谢您提供一些指导。我尝试过

     
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)
     

但是它给了我同样的感觉   像以前一样回答cgo参数   当我有Go指针到Go指针时   试图运行程序

您似乎仍然错过了关键点。使用cgo时,有两个 disjoint “内存视图”:

  • “ Go内存”是Go运行时分配的所有内容,它们为运行的进程提供动力-代表该进程。 GC知道这种内存(大多数情况下,除非有怪异的技巧),这是运行时的一部分。

  • “ C内存”是C代码分配的内存,通常是通过调用libc的{​​{1}} / malloc()来实现的。

现在想象一个不太遥不可及的场景:

  1. 您的程序运行,C“端”被初始化, 产生自己的一个或多个线程,并在它们上保持句柄。
  2. 您的Go“端”已经使用多个线程来运行您的goroutine。
  3. 您在Go代码中分配了一些Go内存并将其传递 到C端。
  4. C端将该内存的地址传递给它自己的一个或多个线程。
  5. 您的程序继续运行, C端线程也是如此-与您的Go代码并行。

因此,您有一个相当经典的场景,在这种情况下,您会获得一个非常简单的情况,即不同步并行内存访问,这肯定会在当今的多核多插槽硬件上遭受灾难。

还认为Go是比C更高级的编程语言。它至少具有自动垃圾回收功能,请注意,the Go spec中没有任何内容指定必须精确指定GC的方式。 这意味着Go的特定实现(包括将来的参考代码)可以自由地允许其GC在内存中移动任意对象¹,这意味着更新指向内存块的每个指针移至原位置后,指向该块新位置中的同一位置。

考虑到这些注意事项,Go开发人员假定为了使realloc()-使用的程序永不过时,²禁止将包含指向其他Go内存块的指针的任何内存块传递给C。 / p>

尽管可以传递包含指向C内存的指针的Go内存块。

回到第二个示例中, 您仍然在Go内存中分配了4个cgodouble数组。

然后,语句a1再次在Go内存中分配cstruct := C.emxArray_real_T{...}的实例,因此,在使用指向Go内存的指针初始化C.emxArray_real_T字段之后,data ),然后将其地址传递给C端,运行时会在实际调用C端并使程序崩溃之前执行其动态检查。


¹例如,这是所谓的“世代”垃圾收集器的典型行为。

²也就是说,您使用同一“主要”版本的Go编译器的将来版本重新编译程序,并且程序可以继续运行,而无需进行修改。