通过Python调用的C函数确定错误的数组大小

时间:2014-12-24 12:01:39

标签: python c arrays ctypes

我正在使用Python来调用从C编译的.so .C代码添加了两个向量,如下所示:

#include <stdio.h>
#include <stdbool.h>

bool add_vectors(const double * const a, const double * const b, double * const c)
{
  if(sizeof(a) != sizeof(b) || sizeof(b) != sizeof(c))
  {
    fprintf(stderr, "Vectors of different lengths cannot be added");
    fprintf(stderr, "Number of elements of a: %d", sizeof(a)/sizeof(double));
    fprintf(stderr, "Number of elements of b: %d", sizeof(b)/sizeof(double));
    fprintf(stderr, "Number of elements of c: %d", sizeof(c)/sizeof(double));
    return false;
  }
  /* Added for diagnostics only; should print 5, does print 1 */
  printf("Size of a: %d\n", sizeof(a)/sizeof(a[0]));
  printf("Size of b: %d\n", sizeof(b)/sizeof(b[0]));
  printf("Size of c: %d\n", sizeof(c)/sizeof(c[0]));

  for(int ii = 0; ii < sizeof(a)/sizeof(double); ++ii)
  {
    c[ii] = a[ii] + b[ii];
  }

  return true;
}

这是通过标准方式编译的

gcc -std=c11 -o add_vectors.so -shared -fPIC add_vectors.c

现在我尝试从以下python代码中调用它:

#!/usr/bin/env python                                                                    
import ctypes
import numpy
add_vectors_lib = ctypes.cdll.LoadLibrary("add_vectors.so")
add_vectors = add_vectors_lib.add_vectors
add_vectors.retype = ctypes.c_bool

array_1d_double = numpy.ctypeslib.ndpointer(dtype = numpy.double, ndim=1, flags="C_CONTIGUOUS")
add_vectors.argtypes = [array_1d_double, array_1d_double, array_1d_double]

#Random vectors to add:
a = numpy.double([1,2,3,4,5])
b = numpy.double([3,4,5,6,7])
#Zero out the return value:
c = numpy.double([0,0,0,0,0])
add_vectors(a, b,c)
print(a)
print(b)
print(c)

但输出是:

Size of a: 1
Size of b: 1
Size of c: 1
[ 1.  2.  3.  4.  5.]
[ 3.  4.  5.  6.  7.]
[ 4.  0.  0.  0.  0.]

如何让C代码识别这些数组的正确大小和/或让Python将数组大小的“知识”传递给C代码?

2 个答案:

答案 0 :(得分:4)

sizeof()是一个编译时运算符。在指针的情况下返回指针本身的实际大小。通常,在32位架构的情况下为4个字节,在64位的情况下为8个。

如果您传递了静态分配的数组的实际变量,它将以字节为单位返回数组的总大小。

答案 1 :(得分:3)

newbee的{​​{1}}问题已在评论中指出。

好吧,为了回答你的问题sizeof()。我试着学习如何在python中编写一个带有C的模块,遵循这个tutorial(我对学习python感兴趣)。

注意:这是一个很长的答案,省略了代码部分。

您编写模块的方式很复杂且容易出错。您需要一个How do I make the C code recognize the proper size of these arrays and/or make the Python pass "knowledge" of the array size to the C code的包装器,它将add_vectors作为参数,因此您可以检查参数的类型(使用PyObject *args)和元素数量(使用PyArg_ParseTuple)数组正确。

这是我的代码的一部分:

add_vectors.c

PyArray_DIM

_add_vectors.c

#include <stdio.h>
#include <stdbool.h>

void add_vectors(const double * const a, const double * const b,
                 double * const c, int len)
{
    int ii;
    for(ii = 0; ii < len; ++ii)
    {
        c[ii] = a[ii] + b[ii];
    }
}

setup.py(使用#include <Python.h> #include <numpy/arrayobject.h> void add_vectors(const double * const a, const double * const b, double * const c, int len); static PyObject *add_vectors_wrapper(PyObject *self, PyObject *args); static PyMethodDef module_methods[] = { {"add_vectors", add_vectors_wrapper, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC init_add_vectors(void) { PyObject *m = Py_InitModule3("_add_vectors", module_methods, NULL); if (m == NULL) return; import_array(); } static PyObject *add_vectors_wrapper(PyObject *self, PyObject *args) { PyObject *x_obj, *y_obj, *z_obj; if (!PyArg_ParseTuple(args, "OOO", &x_obj, &y_obj, &z_obj)) return NULL; /* Interpret the input objects as numpy arrays. */ PyObject *x_array = PyArray_FROM_OTF(x_obj, NPY_DOUBLE, NPY_IN_ARRAY); PyObject *y_array = PyArray_FROM_OTF(y_obj, NPY_DOUBLE, NPY_IN_ARRAY); PyObject *z_array = PyArray_FROM_OTF(z_obj, NPY_DOUBLE, NPY_IN_ARRAY); /* If that didn't work, throw an exception. */ if (x_array == NULL || y_array == NULL || z_array == NULL) { Py_XDECREF(x_array); Py_XDECREF(y_array); Py_XDECREF(z_array); return NULL; } /* How many data points are there? */ int xN = (int)PyArray_DIM(x_array, 0); int yN = (int)PyArray_DIM(y_array, 0); int zN = (int)PyArray_DIM(z_array, 0); /* size check */ if (xN != yN || yN != zN) { fprintf(stderr, "Vectors of different lengths cannot be added\n"); fprintf(stderr, "Number of elements of a: %d\n", xN); fprintf(stderr, "Number of elements of b: %d\n", yN); fprintf(stderr, "Number of elements of c: %d\n", zN); PyObject *ret = Py_BuildValue("s", "Failed"); return ret; } double *x = (double*)PyArray_DATA(x_array); double *y = (double*)PyArray_DATA(y_array); double *z = (double*)PyArray_DATA(z_array); add_vectors(x, y, z, xN); /* Clean up. */ Py_DECREF(x_array); Py_DECREF(y_array); Py_DECREF(z_array); /* Build the output tuple */ PyObject *ret = Py_BuildValue("s", "Success"); return ret; } 运行)

./setup.py build_ext --inplace

addnum.py(一个简单的测试用例)

#!/usr/bin/env python

from distutils.core import setup, Extension
import numpy.distutils.misc_util

setup(
    ext_modules=[Extension("_add_vectors", 
                           ["_add_vectors.c", "add_vectors.c"])],
    include_dirs=numpy.distutils.misc_util.get_numpy_include_dirs(),
)

结果

#!/usr/bin/env python                                                                    
import ctypes
import numpy
from _add_vectors import add_vectors

#Random vectors to add:
a = numpy.double([1,2,3,4])
b = numpy.double([3,4,5,6,7])

#Zero out the return value:
c = numpy.double([0,0,0,0,0])
add_vectors(a, b, c)
print(a)
print(b)
print(c)