【问题标题】:Problem in passing numpy array to a C function将numpy数组传递给C函数的问题
【发布时间】:2022-01-08 14:30:06
【问题描述】:

我有以下 C 函数:

int c_sum(const int *a, int len) {

    int c = 0;
    for (int i=0; i<len; i++){
        c += a[i];
    }
    return c;
}

使用以下内容创建共享库后:

cc -fPIC -shared -o c_sum.so c_sum.c

我在 Jupyter notebook 中运行以下 Python 代码

import numpy as np
import time
from ctypes import *

lib = cdll.LoadLibrary("c_sum.so")
c_sum = lib.c_sum
c_sum.restype = c_int


a = np.random.randint(1,5,6)
print(a)

c = c_sum(c_void_p(a.ctypes.data), c_int(len(a)))
print(c)

它打印以下内容:

[1 4 3 1 2 2]
8

这里可能有什么问题?它打印前 3 个数字的总和。我尝试了不同长度的随机数组,结果发现代码总是对数组中数字的前半部分求和。谢谢。

【问题讨论】:

  • 是的,我发现问题是 Python int 是 4 个字节,而 C int 是 2 个字节,当我将数组的类型从 int 更改为 long 时,它起作用了。但这似乎是一个临时解决方案,我需要找到一种更好的方法来处理这种情况。
  • 您的评论(至少是第一句话)再错误不过了。我在我提到的只是你应该为你的函数指定 argtypesrestype
  • 您需要更改数组的 int 类型。试试a.astype(np.int32)(或者np.int16,如果你的C int真的是16位的话)。
  • @CristiFati 我不是指您的评论。我指的是:是的,这是一个与 C 中为整数类型分配的字节数有关的问题。

标签: python c ctypes


【解决方案1】:

在答案中比在 cmets 中更容易解释。它与不同的 int 大小无关。是的,类型确实不同,但不是 OP 认为的方式,因为 int 数组是一个 NumPy (用 >CC++)之一。查看[SO]: Maximum and minimum value of C types integers from Python 了解有关此主题的更多详细信息。

列表[Python.Docs]: ctypes - A foreign function library for Python
还列出了[NumPy]: C-Types Foreign Function Interface (numpy.ctypeslib) - NumPyCTypes 之间转换的便利库。

dll00.c

#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  define DLL00_EXPORT_API
#endif


#if defined(__cplusplus)
extern "C" {
#endif

DLL00_EXPORT_API int arrSum(const int *arr, int len);

#if defined(__cplusplus)
}
#endif


int arrSum(const int *arr, int len)
{
    int s = 0;
    for (int i = 0; i < len; ++i)
        s += arr[i];
    return s;
}

code00.py

#!/usr/bin/env python

import ctypes as ct
import sys
import numpy as np


IntPtr = ct.POINTER(ct.c_int)

DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")


def main(*argv):
    dll00 = ct.CDLL(DLL_NAME)
    arrSum = dll00.arrSum
    arrSum.argtypes = (IntPtr, ct.c_int)
    arrSum.restype = ct.c_int

    a = np.random.randint(1, 5, 6)
    print(a, sum(a))

    datas = [
        np.ctypeslib.as_ctypes(a),  # Using ctypeslib
        a.ctypes.data_as(IntPtr),  # Using manual conversion
        #IntPtr(ct.c_long(a.ctypes.data)),  # Original way - wrong
    ]
    for data in datas:
        res = arrSum(data, len(a))
        print("\n{0:s} returned: {1:d}".format(arrSum.__name__, res))


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

输出

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q070633293]> sopr.bat
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2019\VC\Auxiliary\Build\vcvarsall.bat" x64 >nul

[prompt]> dir /b
code00.py
dll00.c

[prompt]> cl /nologo /MD /DDLL dll00.c  /link /NOLOGO /DLL /OUT:dll00.dll
dll00.c
   Creating library dll00.lib and object dll00.exp

[prompt]> dir /b
code00.py
dll00.c
dll00.dll
dll00.exp
dll00.lib
dll00.obj

[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" code00.py
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32

[2 2 2 3 4 4] 17

arrSum returned: 17

arrSum returned: 17

Done.

【讨论】:

  • 如果您为将 numpy 数组作为指针的东西设置 argtype,我建议使用 numpy.ctypeslib.ndpointer 而不是通用指针类型。你可以检查python对象上的一堆属性和标志是否正确。
  • @CJR:是的,谢谢您的建议,可以是arrSum.argtypes = (np.ctypeslib.ndpointer(dtype=np.int32, ndim=1, flags='C_CONTIGUOUS'), ct.c_int),而电话,则简单:res = arrSum(a, len(a))
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-03
  • 2018-11-14
  • 2011-01-06
  • 2013-06-26
相关资源
最近更新 更多