我的二维数组在 Ctypes 中不能正常工作

My 2D array does not work properly in Ctypes

main.c:

#include <stdlib.h>

int **function() {
    int **information = malloc(5 * sizeof(int));
    for (int k = 0; k < 5; k++) {
        information[k] = malloc(5 * sizeof(int));
        for (int j = 0; j < 5; j++) {
            information[k][j] = j;
        }
    }
    return information;
}

main.py

import ctypes
from numpy.ctypeslib import ndpointer

lib = ctypes.CDLL("C:\Users\.....\Desktop\test9\a.dll")

lib.function.restype = ndpointer(dtype=ctypes.c_int, shape=(5,5),flags='C')

res = lib.function()
print(res)

结果:

[[222866112       368 222866144       368 222866176]
 [      368 222866208       368 222866672       368]
 [        2         3         4         0 389116888]
 [201333630         0         1         2         3]
 [        4         0 389116888 201333630         0]]

不知道为什么会这样输出,一维模式下同样的方法没问题

我该如何解决这个问题?

已编辑: 两种方法都有用

您的代码将一个包含 5 个指针的数组分配给包含 5 个 int 的数组。指针数组的分配不正确:你应该写 int **information = malloc(5 * sizeof(*information));

而不是 int **information = malloc(5 * sizeof(int));

然而,就您的目的而言,类型 int ** 不正确。您应该分配一个实际的二维数组 int array[5][5].

指向动态分配的二维数组的指针的语法很麻烦,returns这样的指针的函数语法是一个真正的挑战:

#include <stdlib.h>

int (*function(void))[5] {
    // allocate an array of 5 arrays of 5 int
    int (*information)[5] = malloc(5 * sizeof(*information));
    // initialize all 5 rows to identical vectors { 0, 1, 2, 3, 4 }
    for (int k = 0; k < 5; k++) {
        for (int j = 0; j < 5; j++) {
            information[k][j] = j;
        }
    }
    return information;
}

可以使用螺旋规则阅读函数原型:从名称开始,阅读后缀运算符,然后是前缀运算符,在括号处切换方向:

function 是一个没有参数的函数,返回指向 5 int.

数组的指针

要将 ctypesint** 一起使用,您可以使用 POINTER(POINTER(c_int)) 作为类型并使用切片来访问数组的元素。要将 ctypes 与 C-contiguous 5x5 数组一起使用 int*,则可以有效地使用 numpy

test.c

#include <stdlib.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

API int **function() {
    int **information = malloc(5 * sizeof(int*)); // Note row element is int*
    for (int k = 0; k < 5; k++) {
        information[k] = malloc(5 * sizeof(int)); // Note col element is int
        for (int j = 0; j < 5; j++) {
            information[k][j] = k * 5 + j;
        }
    }
    return information;
}

API int *function2() {
    int *information = malloc(5 * 5 * sizeof(int)); // 5 x 5 C contiguous shape
    for (int k = 0; k < 5; k++) {
        for (int j = 0; j < 5; j++) {
            information[k * 5 + j] = k * 5 + j;
        }
    }
    return information;
}

test.py

import ctypes as ct
import numpy as np
from pprint import pprint

lib = ct.CDLL('./test')
lib.function.argtypes = ()
lib.function.restype = ct.POINTER(ct.POINTER(ct.c_int))
lib.function2.argtypes = ()
lib.function2.restype = np.ctypeslib.ndpointer(dtype=ct.c_int, shape=(5, 5), flags='C')

def function():
    res = lib.function()
    # slicing used to extract the correct number of items
    return [row[:5] for row in res[:5]]

pprint(function())
pprint(lib.function2())

输出:

[[0, 1, 2, 3, 4],
 [5, 6, 7, 8, 9],
 [10, 11, 12, 13, 14],
 [15, 16, 17, 18, 19],
 [20, 21, 22, 23, 24]]
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24]])

在这两种情况下,malloc 的内存都已泄漏。理想情况下,让Python管理内存,让C知道形状。使用下面的 np.empty 分配内存而不初始化,然后 C 初始化它。这更灵活,可以与 different-sized 数组一起使用:

test.c

#include <stdlib.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

API void function3(int* information, int row, int col) {
    for (int k = 0; k < row; k++) {
        for (int j = 0; j < col; j++) {
            information[k * row + j] = k * 5 + j;
        }
    }
}

test.py

import ctypes as ct
import numpy as np

lib = ct.CDLL('./test')
lib.function3.argtypes = np.ctypeslib.ndpointer(dtype=ct.c_int),
lib.function3.restype = None

info = np.empty(dtype=ct.c_int,shape=(5,5))
lib.function3(info,5,5)
print(info)

输出:

[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]
 [20 21 22 23 24]]