在 Cython 中通过双指针从 C 函数传递数据

Pass data from a C function via double pointer in Cython

我想在 Cython 中使用一个小的 C 例程。 C函数本身是

#include <stdio.h>
#include "examples.h"

void add_array(int **io_array, int n) {
    int i;
    int *array;

    array = (int *) malloc(n * sizeof(int));

    for(i = 0; i < n; i++) {
       array[i] = i;
    }

    *io_array = array;
}

函数原型:

#ifndef EXAMPLES_H
#define EXAMPLES_H

void add_array(int **io_array, int n);

#endif

现在我想使用 Cython 来连接 C 函数:

cdef extern from "examples.h":
    void add_array(int **io_array, int n)


import numpy as np

def add(arr):
    if not arr.flags['C_CONTIGUOUS']:
        arr = np.ascontiguousarray(arr, dtype=np.int32) 

    cdef int[::1] arr_memview = arr

    add_array(&arr_memview[0], arr_memview.shape[0])

return arr

编译时报错:

pyexamples.pyx:13:14: Cannot assign type 'int *' to 'int **'

连接此功能的正确方法是什么?

它不能与 numpy 数组开箱即用。您必须自己进行内存管理,例如:

%%cython
from libc.stdlib cimport free
def doit():
    cdef int *ptr;
    add_array(&ptr, 5)
    print(ptr[4])
    free(ptr)   #memory management

与您尝试的区别:&arr_memview[0] 是指向整数数组的指针,但是您的函数需要的是指向整数数组指针的指针 - 这就是 &ptr 是什么.


你的函数的问题是,它有太多的责任:

  1. 它分配内存
  2. 初始化内存

如果add_array只做第二部分,那就更简单了,即

void add_array(int *io_array, int n) {
    int i;
    for(i = 0; i < n; i++) {
       io_array[i] = i;
    }
}

因此可以初始化任何内存(也可以是未分配给 malloc 的内存)。


但是,可以使用返回的指针创建一个 numpy 数组 ptr,只是不太直接:

cimport numpy as np
import numpy as np

np.import_array()   # needed to initialize numpy-data structures

cdef extern from "numpy/arrayobject.h":
    void PyArray_ENABLEFLAGS(np.ndarray arr, int flags) #not include in the Cython default include

def doit():
    cdef int *ptr;
    add_array(&ptr, 5)

    # create numpy-array from data:
    cdef np.npy_intp dim = 5
    cdef np.ndarray[np.int32_t, ndim=1] arr = np.PyArray_SimpleNewFromData(1, &dim, np.NPY_INT32, ptr)
    # transfer ownership of the data to the numpy array:
    PyArray_ENABLEFLAGS(arr, np.NPY_OWNDATA)
    return arr

以下值得一提:

  1. np.import_array() is needed to be able to use all of the numpy's functionality. 如果不调用 np.import_array() 会发生什么。
  2. PyArray_SimpleNewFromData之后,数据本身不属于生成的numpy数组,因此我们需要启用OWNDATA-flag,否则会发生内存泄漏。
  3. 生成的 numpy 数组可以负责释放数据并不明显。例如,可以使用 Python's memory allocator.
  4. 而不是使用 malloc/free

我想详细说明上面的第3点。 Numpy 使用特殊函数 allocate/deallocate 存储数据 - 它是 PyDataMem_FREE and uses system's free for it. So in your case (using system's malloc/free in add_array) everything is Ok. (PyDataMem_FREE should not be confused with PyArray_free, as I did in an earlier version of the answer. PyArray_free is responsible for freeing other elements (array itself, and dimension/strides data, not data-memory) of the numpy-array, see here 并且根据 Python 版本不同)。

更 flexible/safe 的方法是使用 PyArray_SetBaseObject,如 SO-post 所示。