如何使用Cython处理C函数返回的二维数组?
在Cython中接收C函数返回的二维int数组
你已经搞定了一维numpy数组传C函数的操作,现在要处理C返回的int**二维数组,其实核心是把C的指针结构转换成numpy能识别的数组,还要注意内存泄漏的问题,我给你一步步拆解:
首先先补全你那没写完的C代码(加上测试数据和配套的释放函数,这很重要):
// your_array_code.h 或者.c文件里的内容 #include <stdlib.h> int **make_array() { int nrows = 3; int ncols = 5; // 先分配存储行指针的数组 int **ret = malloc(nrows * sizeof(int *)); // 为每一行分配数据内存并填充测试值 for (int i = 0; i < nrows; i++) { ret[i] = malloc(ncols * sizeof(int)); for (int j = 0; j < ncols; j++) { ret[i][j] = i * ncols + j; // 比如生成0-14的连续数 } } return ret; } // 必须配套释放函数,不然会内存泄漏 void free_array(int **arr, int nrows) { for (int i = 0; i < nrows; i++) { free(arr[i]); } free(arr); }
接下来分两种常用的处理方式,推荐第一种:
方式一:复制到连续的numpy数组(首选)
numpy最偏好连续内存的数组,操作效率更高,而且内存管理更省心:
在你的Cython文件(比如array_wrapper.pyx)里这么写:
import numpy as np cimport numpy as np # 声明C函数的接口,要对应你的头文件路径 cdef extern from "your_array_code.h": int** make_array() void free_array(int** arr, int nrows) def get_c_2d_array(): cdef int** c_arr cdef int nrows = 3, ncols = 5 cdef np.ndarray[np.int_t, ndim=2] np_arr # 调用C函数拿到二维指针数组 c_arr = make_array() # 创建一个空的numpy二维数组,指定好形状和类型 np_arr = np.empty((nrows, ncols), dtype=np.int32) # 逐行逐列把C数组的数据复制到numpy数组里 cdef int i, j for i in range(nrows): for j in range(ncols): np_arr[i, j] = c_arr[i][j] # 重点!用完C的数组一定要释放内存,不然会泄漏 free_array(c_arr, nrows) return np_arr
方式二:直接包装非连续内存(不推荐,仅特殊场景用)
如果你的数据特别大,不想复制,可以直接让numpy包装C的内存,但要注意内存管理的坑:
def get_c_2d_array_no_copy(): cdef int** c_arr cdef int nrows = 3, ncols = 5 cdef np.ndarray[np.int_t, ndim=2] np_arr c_arr = make_array() # 先创建一个数组存行指针,防止C的指针被Python的GC干扰 row_ptrs = np.empty(nrows, dtype=np.intp) for i in range(nrows): row_ptrs[i] = <np.intp>c_arr[i] # 创建numpy数组,用行指针数组作为缓冲区,指定非连续的步长 np_arr = np.ndarray( shape=(nrows, ncols), dtype=np.int32, buffer=row_ptrs, strides=(sizeof(np.intp), sizeof(np.int32)) ) # 这里要注意:numpy不会自动释放C的内存,所以得手动处理 # 我这里返回数组和对应的释放函数,你用完数组后要调用这个释放函数 return np_arr, lambda: free_array(c_arr, nrows)
几个关键注意事项:
- 内存泄漏是头号敌人:C函数用
malloc分配的内存,Python的GC管不到,所以必须调用配套的free_array函数释放 - 类型要匹配:如果C里用的是
long而不是int,那numpy要对应np.int64,不然会出现类型错误 - 连续vs非连续:方式一的数组是连续的,能正常使用numpy的所有操作;方式二的数组是非连续的,部分numpy函数会变慢甚至出错,除非你确定必须用这种方式
内容的提问来源于stack exchange,提问作者plx
相关产品推荐
相关产品推荐

