【问题标题】:SegFault when trying to write to a Numpy array created within a C Extension尝试写入在 C 扩展中创建的 Numpy 数组时出现 SegFault
【发布时间】:2011-10-17 18:06:13
【问题描述】:

我在 for 循环中有一个 if 子句,我在其中预先定义了 state_out:

state_out = (PyArrayObject *) PyArray_FromDims(1,dims_new,NPY_BOOL);

而if条件是这样的:

        if (conn_ctr<sum*2){
            *(state_out->data + i*state_out->strides[0]) =  true;
        }
        else {
            *(state_out->data + i*state_out->strides[0]) =  false;
        }

当注释掉这些时,state_out 作为一个全错误的 Numpy 数组返回。这个作业有一个我看不到的问题。据我所知,这段代码中调用的 struct PyArrayObject 中都是指针,所以经过指针运算后,应该是指向了我要写的地址。 (代码中的所有 if 条件都是通过以这种方式获取值来构建的,并且我知道它有效,因为我设法 printf 输入数组的值。)然后,如果我想为内存中的这些部分之一分配一个布尔值,我应该通过*(pointer_intended) = true 分配它我错过了什么?

编辑:我发现即使我没有达到这些值,即使我在其中放置了一些 printf 函数:

if (conn_ctr<sum*2){
    printf("True!\n");
}
else {
    printf("False!\n");
}

我又遇到了 SegFault。

非常感谢,剩下的代码在这里。

#include <Python.h>
#include "numpy/arrayobject.h"
#include <stdio.h>
#include <stdbool.h>

static PyObject* trace(PyObject *self, PyObject *args);

static char doc[] =
"This is the C extension for xor_masking routine. It interfaces with Python via C-Api, and calculates the"
"next state with C pointer arithmetic";

static PyMethodDef TraceMethods[] = {
    {"trace", trace, METH_VARARGS, doc},
    {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC
inittrace(void)
{
    (void) Py_InitModule("trace", TraceMethods);
    import_array();
}

static PyObject* trace(PyObject *self, PyObject *args){
    PyObject *adjacency ,*mask, *state;
    PyArrayObject *adjacency_arr, *mask_arr, *state_arr, *state_out;

    if (!PyArg_ParseTuple(args,"OOO:trace", &adjacency, &mask, &state)) return NULL;

    adjacency_arr = (PyArrayObject *)
        PyArray_ContiguousFromObject(adjacency, NPY_BOOL,2,2);

    if (adjacency_arr == NULL) return NULL;
    mask_arr = (PyArrayObject *)
        PyArray_ContiguousFromObject(mask, NPY_BOOL,2,2);

    if (mask_arr == NULL) return NULL;
    state_arr = (PyArrayObject *)
        PyArray_ContiguousFromObject(state, NPY_BOOL,1,1);

    if (state_arr == NULL) return NULL;

    int dims[2], dims_new[1];
    dims[0] = adjacency_arr -> dimensions[0];
    dims[1] = adjacency_arr -> dimensions[1];
    dims_new[0] =  adjacency_arr -> dimensions[0];
    if (!(dims[0]==dims[1] && mask_arr -> dimensions[0] == dims[0]
                         && mask_arr -> dimensions[1] == dims[0]
                         && state_arr -> dimensions[0] == dims[0]))
                         return NULL;


    state_out = (PyArrayObject *) PyArray_FromDims(1,dims_new,NPY_BOOL);

    int i,j;

    for(i=0;i<dims[0];i++){
        int sum = 0;
        int conn_ctr = 0;

            for(j=0;j<dims[1];j++){

                bool adj_value = (adjacency_arr->data + i*adjacency_arr->strides[0]
                         +j*adjacency_arr->strides[1]);

                if (*(bool *) adj_value == true){

                    bool mask_value = (mask_arr->data + i*mask_arr->strides[0]
                    +j*mask_arr->strides[1]);
                    bool state_value = (state_arr->data + j*state_arr->strides[0]);

                    if ( (*(bool *) mask_value ^ *(bool *)state_value) ==  true){
                        sum++;
                    }
                    conn_ctr++;
                }
            }

            if (conn_ctr<sum*2){

            }
            else {

            }
    }

    Py_DECREF(adjacency_arr);
    Py_DECREF(mask_arr);
    Py_DECREF(state_arr);
    return PyArray_Return(state_out);
}

【问题讨论】:

  • 您正在使用bool 变量来存储指针/地址。 NPY_BOOL (unsigned char) 和 bool 在您的平台上可能不一样。
  • 我尝试将 true 或 false 进行类型转换:(unsigned char) true 也不太顺利。
  • 我也尝试分配 1 或 0,这是我猜我之前是怎么做的。 *(state_out-&gt;data + i*state_out-&gt;strides[0]) = 1; 也因 SegFault 而失败。
  • 请贴出完整的Python测试代码,包括输入数据和预期输出。
  • @cgohlke 的上述评论意味着你应该写例如npy_bool *adj_value = ... 而不是 bool adj_value = ... 在处理指向布尔值的指针时。

标签: python c python-c-api


【解决方案1】:
    if (conn_ctr<sum*2){
        *(state_out->data + i*state_out->strides[0]) =  true;
    }
    else {
        *(state_out->data + i*state_out->strides[0]) =  false;
    }

这里,我天真地做了一个指针运算,state_out->data是一个指向数据开头的指针,它被定义为char:SciPy Doc - Python Types and C-Structures的指针

typedef struct PyArrayObject {
    PyObject_HEAD
    char *data;
    int nd;
    npy_intp *dimensions;
    npy_intp *strides;
    ...
} PyArrayObject;

我在这里复制了哪一部分。 state_out->strides 是一个指向长度为我们所拥有的数组维度的数组的指针。在这种情况下,这是一个一维数组。所以当我做指针算术(state_out-&gt;data + i*state_out-&gt;strides[0])时,我的目标当然是计算指向数组第i个值的指针,但是我没有给出指针的类型,所以

我试过了:

NPY_BOOL *adj_value_ptr, *mask_value_ptr, *state_value_ptr, *state_out_ptr;

哪些变量指向我对我的 for 循环感兴趣的值,而 state_out_ptr 是我要写的那个。我以为自从我声明 这些数组的组成部分是NPY_BOOL 类型,指向数组中数据的指针也是NPY_BOOL 类型。 当使用直接操作内存的数据时,这将失败并出现 SegFault。这是因为 NPY_BOOL 是一个整数的 enum (如 pv 在 cmets 中友好地说明),供 NumPy 在内部使用。有一个 C typedef npy_bool 以便在代码中使用布尔值。 Scipy Docs。当我用

类型介绍我的指针时
npy_bool *adj_value_ptr, *mask_value_ptr, *state_value_ptr, *state_out_ptr;

分段错误消失了,我成功操作并返回了一个 Numpy 数组。

我不是专家,但这解决了我的问题,如果我错了,请指出。

源码中改变的部分是:

state_out = (PyArrayObject *) PyArray_FromDims(1,dims_new,NPY_BOOL);

npy_bool *adj_value_ptr, *mask_value_ptr, *state_value_ptr, *state_out_ptr;
npy_intp i,j;

for(i=0;i<dims[0];i++){
    npy_int sum = 0;
    npy_int conn_ctr = 0;

        for(j=0;j<dims[1];j++){

            adj_value_ptr = (adjacency_arr->data + i*adjacency_arr->strides[0]
                     +j*adjacency_arr->strides[1]);

            if (*adj_value_ptr == true){

                mask_value_ptr = (mask_arr->data + i*mask_arr->strides[0]
                +j*mask_arr->strides[1]);

                state_value_ptr = (state_arr->data + j*state_arr->strides[0]);

                if ( (*(bool *) mask_value_ptr ^ *(bool *)state_value_ptr) ==  true){
                    sum++;
                }
                conn_ctr++;
            }
        }
        state_out_ptr = (state_out->data + i*state_out->strides[0]);
        if (conn_ctr < sum*2){
            *state_out_ptr =  true;
        }
        else {
            *state_out_ptr =  false;
        }
}

【讨论】:

  • 在某些平台上代码仍然可能会失败,因为 1) 将 npy_bool* 转换为 bool* 和 2) 使用 int 而不是 npy_intp 用于尺寸/计数器
  • 是的,我应该在所有条件下都使用 npy_bool*。这真的很有帮助,我会试试的。
  • 但是 I XOR 没有为 npy_bools 定义,所以我至少必须对其他的进行类型转换,以避免出现 4 个 if 子句的序列。我改变了答案中的所有其他人。
  • npy_bool 是 C 类型定义。 NPY_BOOL 是一个枚举成员(即一个整数),Numpy 使用它来识别该类型的数组。
猜你喜欢
  • 2013-09-12
  • 1970-01-01
  • 2014-10-19
  • 2017-12-22
  • 1970-01-01
  • 2021-07-22
  • 2016-09-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多