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

标签 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;
}

和函数原型(prototype):

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


但是,可以使用返回的指针 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

以下是值得一提的:

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

我想详细说明上面的第 3 点。 Numpy 使用一个特殊的函数来为数据分配/释放内存——它是 PyDataMem_FREE并为其使用系统的免费。因此,在您的情况下(在 add_array 中使用系统的 malloc/free)一切正常。 (PyDataMem_FREE 不应与 PyArray_free 混淆,正如我在早期版本的答案中所做的那样。PyArray_free 负责释放其他元素(数组本身和维度/跨越 numpy 数组的数据,而不是数据内存,see here 并且根据 Python 版本而有所不同)。

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

关于c - 在 Cython 中通过双指针从 C 函数传递数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51546710/

相关文章:

C:是否可以将指针关联到结构成员?

c - 如何获取字符串的大小以与预处理器进行比较

c - C中重叠对象的语义是什么?

Python 打包 : Boost library as dependency

python - 如何为多个Python版本和平台构建编译模块

c - 黑客如何利用数组或指针

c - 返回 C 中未知大小的数组并使用终止值

arrays - 将一个数组选项取出一个周期

c# - foreach 是否在每次迭代时评估数组?

c++ - Cython 中的抽象类(带有纯虚方法)