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

时间:2018-07-26 20:04:41

标签: c arrays cython cythonize

我想对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 **'

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

1 个答案:

答案 0 :(得分:2)

它不适用于numpy-arrays。您必须自己进行内存管理,例如:

%%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。 / p>


您的功能存在的问题是,它的职责过多:

  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分配的内存)。


但是,可以使用返回的指针ptr创建一个numpy数组,这并不那么简单:

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

以下值得一提:

    要使用numpy的所有功能,需要
  1. np.import_array()。如果没有调用np.import_array()Here is an example会发生什么。
  2. PyArray_SimpleNewFromData之后,数据本身不归最终的numpy数组所有,因此我们需要启用OWNDATA标志,否则会发生内存泄漏。
  3. 这并不明显,产生的numpy-array可以负责释放数据。例如,可以使用Python's memory allocator代替使用malloc / free。

我想详细说明上面的第3点。 Numpy使用特殊功能为数据分配/释放内存-它是PyDataMem_FREE,并使用系统的free。因此,就您而言(在add_array中使用系统的malloc / free),一切正常。 ({PyDataMem_FREE不应与PyArray_free混淆,就像我在较早版本的答案中所做的那样。PyArray_free负责释放其他元素(数组本身,以及维/跨度数据,而不是数据存储器)see here,并且根据Python版本而有所不同。

一种更加灵活/安全的方法是使用SO-post所示的PyArray_SetBaseObject