【问题标题】:How can I create and pass as argument a multidimensional ctypes array?如何创建多维 ctypes 数组并将其作为参数传递?
【发布时间】:2020-11-06 10:42:36
【问题描述】:

我有一个共享库,其中的函数接受 int **,如下所示:

void printarray(int **array, int n, int m)
{
        int i, j;
        for (i = 0; i < m; i++)
                for (j = 0; j < n; j++)
                        printf("%d\n", array[i][j]);
}

我想用ctypes 从 Python 调用函数。为此,我编写了以下内容:

from ctypes import *

cdll.LoadLibrary("libssa.so")
libssa = CDLL("libssa.so")

a = [[1, 2, 3], [4, 5, 6]]
 
T = ((c_int * 3) * 2)
array = T()
for i in range(2):
    for j in range(3):
        array[i][j] = a[i][j]

libssa.printarray(array, c_int(3), c_int(2))

我预计T 类型将是指向ints 数组的指针数组,而array 将是指向该类型对象的指针。

但是,只要在 C 代码中访问 array,就会发生分段错误。特别是,对于这个例子,valgrind 指向这条线:printf("%d\n", array[i][j]); 作为段错误的来源。

问题是,构造可用作int ** 类型参数的ctypes 对象的正确方法是什么?

【问题讨论】:

  • “我预计类型 T 将是一个指向整数数组的指针数组,而该数组将是指向该类型对象的指针。”它是,但仅仅因为你有指向整数数组的指针,并不意味着那些指向整数数组的指针实际上有它们指向的整数数组。但无论如何,多维数组的工作方式与 C 中的不同。

标签: python c python-3.x ctypes


【解决方案1】:

请注意int** 不等于int[][](请参阅this post)。这是由于多维数组的内部结构:它可以表示为

                      +-------------+
array ->  array[0] -> | array[0][0] |
                      +-------------+
                      | array[0][1] |
                      +-------------+
                      |     ...     |
                      +-------------+
          array[1] -> | array[1][0] |
                      +-------------+
                            ...      

每个方块显示如何访问相应的值。但是int** 会是这样的结构

         +------+      +-----+
int** -> | int* | ---> | int |
         +------+      +-----+
         | int* | -\   | int |
         +------+  |   +-----+
           ...     |   | ... |
                   |   +-----+
                   |
                   |   +-----+
                   \-> | int |
                       +-----+
                         ... 

每个方块应该代表一个内存位置,其中包含类型。与 Python 相比,数组的大小不保存在 c 中,因此当指针作为参数传递时,使用 array[i][j] 访问会失败。最好用一维数组,自己计算第二维,所以

void printarray(int *array, int n, int m)
{
        int i, j;
        for (i = 0; i < m; i++)
                for (j = 0; j < n; j++)
                        printf("%d\n", array[i + j*m]);
}

array[j + i*n],具体取决于您的实施。

【讨论】:

  • 问题不只是本质上没有跟踪尺寸; OP 代码中的问题是主数组中的 int*s 没有自己指向的分配。
【解决方案2】:

指针数组不同于二维数组。这就是你的函数。

DLL 代码(针对 Windows 进行了修改,并且 for 循环中的 m/n 是向后的):

#include <stdio.h>

__declspec(dllexport)
void printarray(int **array, int n, int m)
{
        int i, j;
        for (i = 0; i < n; i++)
                for (j = 0; j < m; j++)
                        printf("array[%d][%d] = %d\n", i, j, array[i][j]);
}

test.py:

from ctypes import *

dll = CDLL('./test')
dll.printarray.argtypes = POINTER(POINTER(c_int)),c_int,c_int
dll.printarray.restype = None

def wrap(a):
    x = len(a)
    y = len(a[0])
    arr = (POINTER(c_int) * x)()  # allocate array of pointers in X dimension
    for i in range(x):
        arr[i] = (c_int * y)(*a[i]) # allocate array in Y dimension and populate it
    return arr,x,y

a = [[1,2,3],[4,5,6]]

dll.printarray(*wrap(a))

输出:

array[0][0] = 1
array[0][1] = 2
array[0][2] = 3
array[1][0] = 4
array[1][1] = 5
array[1][2] = 6

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-15
    • 1970-01-01
    • 2022-01-09
    • 2012-11-24
    • 1970-01-01
    相关资源
    最近更新 更多