如何使用ctypes将C函数返回的2维数组传递给Python?

4

我有一个返回二维数组(实际上是double**)的C函数,我想使用ctypes将其传递给Python。关于此问题有一些疑问,但是数组在参数列表中被修改而不是使用函数的返回值。以下是我的代码(包括C函数和相应的Python封装器)。

double** flux_function_2d(double *u, double gamma){

  double **F;
  double p, H;

  F = (double **)malloc(4*sizeof(double *));
  for (int i = 0; i < 4; i++)
    F[i] = (double *)malloc(2*sizeof(double));

  p = (gamma - 1.0) * (u[3] - 0.5 * (u[1]*u[1] + u[2]*u[2]) / u[0]);
  H = u[3] / u[0] + p / u[0];

  F[0][0] = u[1]; F[1][0] = u[1] * u[1] / u[0] + p;
  F[2][0] = u[1] * u[2] / u[0]; F[3][0] = u[1] * H;

  F[0][1] = u[2]; F[1][1] = u[2] * u[1] / u[0];
  F[2][1] = u[2] * u[2] / u[0] + p; F[3][1] = u[2] * H;

  return F;
}


def c_flux_function_2d(u, gamma):
    flux = np.ctypeslib.load_library("libs/flux.so", ".")
    flux.flux_function_2d.argtypes = [POINTER(c_double), c_double]
    flux.flux_function_2d.restype = POINTER(POINTER(c_double))
    F = flux.flux_function_2d(u.ctypes.data_as(POINTER(c_double)),
        c_double(gamma))
    F_arr = np.ctypeslib.as_array(F, shape=(4, 2))
    return F_arr

错误发生在 F_arr = np.ctypeslib.as_array(F, shape=(4, 2)),这意味着 numpy 无法解析 ctypes 中指针的指针。
提前感谢!

double** 不是一个二维数组。 - user2357112
double** 是一个数组的数组。使用这个答案中的方法可能会更容易:https://dev59.com/MWXWa4cB1Zd3GeqPRN0C#11385138(定义一个带有多维数组的结构体,并传递指向该结构体的指针)。 - Nick ODell
1
@NickODell:它也不是一个数组的数组。它只是一个指针,恰好可以使用与数组的数组相同的语法进行索引。所涉及的内存布局与数组的数组完全不同。 - user2357112
“传递给Python”是什么意思?你总是可以通过F_arr [i] [j]访问Python中的数据。您想将其用作numpy数组吗?如果是,由于不同的内存布局,您需要将数据复制到numpy数组中。 - ead
你打算如何释放已返回的内存?修改作为参数传递的数组意味着Python管理内存,因此您不必在C函数中处理释放分配的内存。 - Mark Tolonen
1个回答

3
如果您实际上有一个二维数组,您可以调用numpy.ctypeslib.as_array,但您没有。
您有一个指向其他数组的第一个元素的指针数组的第一个元素的指针。这与二维数组完全不同,只是在C中使用相同的语法进行索引。
您拥有的内存布局与NumPy完全不兼容。NumPy数组具有单个内存缓冲区,其中包含形状和步幅信息,告诉您可以在每个维度上走多远以及在每个维度上采取一步需要多少字节。
对于C连续数组(最常见的情况),MxN数组的缓冲区的内存布局与C whatevertype[M][N]相同。相反,您的数据结构的子数组分散在任意的内存位置,第一维没有步幅。
您需要将数据放入NumPy可以处理的布局中。有各种方法可以做到这一点,包括仅在C函数中分配并处理连续的缓冲区,或创建一个numpy.empty([4, 2]),并使用嵌套循环将数据复制到其中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接