C语言 指针类型与PyArray_SimpleNew不匹配



我正在使用C API为Numpy为Python创建一个模块,并遇到了与PyArray_SimpleNew输出的奇怪不兼容,我想了解这一点。但首先举一个最小的例子:

# include <Python.h>
# include <numpy/arrayobject.h>
void foo()
{
    Py_Initialize();
    import_array();
    npy_intp dims[1] = {42};
    PyObject * A = PyArray_SimpleNew(1,dims,NPY_DOUBLE); // Line A
    Py_Finalize();
}
int main()
{
    foo();
    return 0;
}

如果我用 gcc source.c -lpython2.7 -I/usr/include/python2.7 --pedantic 编译它,我得到(参考 A 行):

ISO C 禁止将对象指针转换为函数指针类型

因此,显然,出于某种原因,PyArrayObject应该是函数指针。


根据文档(例如,这里),PyArray_SimpleNew返回的类型为 PyObject *,因此上述内容应该完全没问题。此外,我没有得到其他返回PyObject *函数的类似警告。

现在,虽然这只是我们正在谈论的一个警告,并且我的程序使用PyArray_SimpleNew按预期工作,但所有这些都表明 Numpy C API 没有像我认为的那样工作(或有一个错误)。因此,我想了解这背后的原因。


我在以下系统上制作了上述内容:

    GCC 4.7.2
  • (Debian 4.7.2-5), Numpy 1.6.2
  • GCC 4.8.2
  • (Ubuntu 4.8.2-19ubuntu1), Numpy 1.8.2

在这两种情况下,情况都会随着# define NPY_NO_DEPRECATED_API NPY_1_8_API_VERSION而改变。

为了回答您为什么收到有关"ISO C 禁止将对象指针转换为函数指针类型"的警告的问题,我检查了 numpy 的源代码。

PyArray_SimpleNew 是第 125 行numpy/ndarrayobject.h中定义的宏:

#define PyArray_SimpleNew(nd, dims, typenum) 
        PyArray_New(&PyArray_Type, nd, dims, typenum, NULL, NULL, 0, 0, NULL)

这会将 A 行扩展为:

PyObject * A = PyArray_New(&PyArray_Type, 1, dims, typenum, NULL, NULL, 0, 0, NULL); // Line A

PyArray_New本身就是第 1017 行numpy/__multiarray_api.h中定义的宏:

#define PyArray_New 
        (*(PyObject * (*)(PyTypeObject *, int, npy_intp *, int, npy_intp *, void *, int, int, PyObject *)) 
         PyArray_API[93])

这会将 A 行扩展为:

PyObject * A = (*(PyObject * (*)(PyTypeObject *, int, npy_intp *, int, npy_intp *, void *, int, int, PyObject *))
                PyArray_API[93])(&PyArray_Type, 1, dims, typenum, NULL, NULL, 0, 0, NULL); // Line A

这个复杂的表达式可以简化为:

// PyObject * function93(PyTypeObject *, int, npy_intp *, int, npy_intp *, void *, int, int, PyObject *)
typedef PyObject * (*function93)(PyTypeObject *, int, npy_intp *, int, npy_intp *, void *, int, int, PyObject *);
// Get the PyArray API function #93, cast the function pointer to its
// signature, and call it with the arguments to `PyArray_New`.
PyObject * A = (*(function93) PyArray_API[93])(&PyArray_Type, 1, dims, typenum, NULL, NULL, 0, 0, NULL); // Line A

导致禁止转换的部分是:

*(function93) PyArray_API[93]

在 807、810 和 812 行的numpy/__multiarray_api.h中,PyArray_API声明为void ** .所以PyArray_API[93]是一个void *(即一个对象指针),它被强制转换为函数指针。

我不太熟悉NumPy或其C-api,但看起来你是正确使用它。NumPy恰好使用了一些非标准的,未定义的GCC 支持但 ISO 标准不支持的内部行为(即 NumPy 不能通过 ISO 标准移植)。

参见 [SciPy-User] NumPy C API:将指针投射到对象到指针到函数从何而来?

最新更新