【问题标题】:python use ctypes to pass 2-d array into c functionpython使用ctypes将二维数组传递给c函数
【发布时间】:2020-07-17 10:52:58
【问题描述】:

我想用 C 来处理一些计算。比如我有一个添加两个矩阵的C函数:

// mat_add.c
#include <stdlib.h>

void matAdd(int ROW, int COL, int x[][COL], int y[][COL], int z[][COL]){
    int i, j;
    for (i = 0; i < ROW; i++){
        for (j = 0; j < COL; j++){
            z[i][j] = x[i][j] + y[j][j];
        }
    }
}

然后我编译成.so文件:
gcc -shared -fPIC mat_add.c -o mat_add.so

在python中:

# mat_add_test.py
import ctypes
import numpy as np

def cfunc(x, y):
    nrow, ncol = x.shape
    
    objdll = ctypes.CDLL('./mat_add.so')
    
    func = objdll.matAdd
    func.argtypes = [
        ctypes.c_int,
        ctypes.c_int,
        np.ctypeslib.ndpointer(dtype=np.int, ndim=2, shape=(nrow, ncol)),
        np.ctypeslib.ndpointer(dtype=np.int, ndim=2, shape=(nrow, ncol)),
        np.ctypeslib.ndpointer(dtype=np.int, ndim=2, shape=(nrow, ncol))
    ]
    func_restype = None
    
    z = np.empty_like(x)
    func(nrow, ncol, x, y, z)
    return z


if __name__ == '__main__':
    x = np.array([[1, 2], [3, 4]], dtype=np.int)
    y = np.array([[2, 2], [5, 6]], dtype=np.int)
    z = cfunc(x, y)
    print(z)
    print('end')

执行这个python文件,我得到:

$ python mat_add_test.py 
[[                  3                   4]
 [8386863780988286322 7813586346238636153]]
end

返回矩阵的第一行是正确的,但第二行是错误的。我想我没有成功更新z中的值,但我不知道问题出在哪里。
任何人都可以帮忙吗?非常感谢!

【问题讨论】:

  • 您在 C 中输入的长度错误。如果我尝试使用声明为long 的数组的代码,它会起作用。可能有一些正确的过程和适当的typedef - 我不建议硬编码long,但它足以证明这一点。
  • 除此之外,C: y[j][j] 中的一个小错误应该是 y[i][j]
  • x.dtype 报告dtype('int64')
  • 我将我的 .c 文件从 int 更改为 long,它可以工作了!我忘记了 np.int 与 c 中的 int 不同。非常感谢!
  • 不客气。为了发现问题,我只是将 printf 语句放入 C 代码中 - printf("i=%d j=%d x=%d y=%d\n", i, j, x[i][j], y[i][j]); 写入 i=0 j=0 x=1 y=2 i=0 j=1 x=0 y=0 i=1 j=0 x=2 y=2 i=1 j=1 x=0 y=0。那么它就变得相当明显了。

标签: python c ctypes


【解决方案1】:

问题中二维数组的处理是正确的。唯一的问题(除了 C 代码如何索引 y 数组的拼写错误 - y[j][j] 应该是 y[i][j])是 np.intnp.int64,所以这不对应于 C int

为确保类型匹配,可以在两种语言中指定显式长度。

在 Python 中:显式使用 np.int32np.int64(而不是 np.int)。

在 C 中:#include &lt;stdint.h&gt;,然后相应地使用int32_tint64_t(可能通过typedef),而不是int

然后问题就消失了。

对于ROWCOL,它们是按值调用的,因此不太重要(当然前提是值不会溢出)。

这里发生了什么

实际上,二维数组仍然只是内存中的一维值序列;二维只是索引它的一种方便方式。

所以在 numpy 中,调用 C 之前的数组是(十六进制):

0000000000000001 0000000000000002 0000000000000003 0000000000000004  <== x
0000000000000002 0000000000000002 0000000000000005 0000000000000006  <== y
UUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUU  <== z 

其中U 表示未定义/未初始化的数据

但在 C 代码中(假设为小端序),将数组视为 32 位,它会看到:

inputs
00000001 00000000 00000002 00000000 00000003 00000000 00000004 00000000  <== x
00000002 00000000 00000002 00000000 00000005 00000000 00000006 00000000  <== y
UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU  <== z at start

然后 C 代码循环遍历每个元素的前 4 个元素,执行加法,因此产生:

00000003 00000000 00000004 00000000 UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU  <== z at end

然后使用 64 位 int 类型返回 numpy,现在我们看到:

0000000000000003 0000000000000004 UUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUU  <== output z

解释为二维数组,这是array([[3, 4], [whatever, whatever]])

【讨论】:

  • 我完全理解为什么我错了,为什么会出现一些奇怪的数字。感谢您耐心的解释!
  • @Chun-YeLu 我在答案中用错误的长度写了解释——16 和 32 位(4 和 8 位十六进制数字)——现在更正为 32 和 64 位(8 和 16 位十六进制数字) --虽然原理当然是一模一样的。
  • 没关系;)我不熟悉十六进制,所以我没有注意到这一点。
猜你喜欢
  • 2019-06-20
  • 2021-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多