通过ctypes从Python调用的C函数返回不正确的值

pod7payv  于 2022-12-17  发布在  Python
关注(0)|答案(1)|浏览(182)

我用C语言写了一个简单的函数,它对一个给定的数字进行幂运算,当我用C语言调用它时,这个函数返回正确的值,但是当我用Python调用它时,它返回了一个不同的、不正确的值。
我使用以下命令创建了共享文件:$ gcc -fPIC -shared -o test.so test.c
我尝试了C函数的不同配置,有些返回预期值,有些不返回。例如,当我的函数使用return x*x返回一个简单的正方形,而没有for循环时,它在Python中返回了正确的值。
我希望最终能够在python中调用一个返回二维C数组的C函数。

#include <stdio.h>

float power(float x, int exponent)
{
    float val = x;
    for(int i=1; i<exponent; i++){
        val = val*x;
    }
    return val;
}
from ctypes import *

so_file = '/Users/.../test.so'
functions = CDLL(so_file)

functions.power.argtype = [c_float, c_int]
functions.power.restype = c_float

print(functions.power(5,3))

当我在C中调用该函数时,我获得了125. 0的预期输出,但当我在Python中调用该函数时,它返回了0. 0的值。
这是我第一次使用ctypes。我是否犯了一个明显的错误,导致函数计算错误?

gg0vcinb

gg0vcinb1#

列表[Python.Docs]:ctypes -Python的外来函数库。
为了在调用函数(驻留在 .dll.so)中)时正确地转换所有内容(Python〈=〉C),需要指定两件事(将 x86(* pc 032 )调用约定(Win)放在一边):
1.参数类型
1.返回类型
CTypes 中,这是通过指定以下内容实现的:
1.argtypes-包含每个函数的参数(CTypes)类型的序列(元组,列表),按照它们在函数声明中出现的顺序(如果函数只有一个参数,则应该有一个元素序列
1.restype-单个 CTypes 类型(函数的返回类型)
旁注:上述方法的替代方法是对外来函数进行原型化(CFUNCTYPEWINFUNCTYPEPYFUNCTYPE-检查
函数原型*部分(在开头的 URL 中))。
总之:

*没有指明
*拼写错误(基本上与前面的项目符号相同)
其中任何一项(需要时(1)),将导致应用默认值:所有都被视为(C89 风格)ints,其中(在大多数系统上)**为32位长。

这会产生 Undefined Behavior(2)(当错误地指定它们时也适用),特别是在 * 064 bitCPU* / OS(和 Python 进程)上,其中较大类型的值(例如指针)可能会被截断(检查[SO]: Maximum and minimum value of C types integers from Python (@CristiFati's answer)以了解有关 C 整型的详细信息(限制-〉大小))。
显示的错误可能很多,有时会引起误解。
您拼错了***argtype***(末尾缺少***s***)。
改正一下,你就没事了

示例

对于由 libdll.dlllibdll.so)导出的具有以下头文件的函数 func

double func(uint32_t ui32,
            float f,
            long long vll[8],
            void *pv,
            char *pc,
            uint8_t *pui8);
  • Python* 的等价形式为:
import ctypes as cts

func = libdll.func
func.argtypes = (cts.c_uint32,
                 cts.c_float,
                 cts.c_longlong * 8,
                 cts.c_void_p,
                 cts.c_char_p,
                 cts.POINTER(cts.c_ubyte))
'''
Last 2 argument types are almost the same (1st is signed while 2nd is unsigned).
  - 1st is used for NUL terminated strings (that work with strcpy, strlen, ...).
    Python automatically converts them to bytes

  - 2nd is used for buffers in general (which may contain NUL (0x00) characters)
'''

func.restype = cts.c_double

相同(或非常相似)情景的一些(更具破坏性)结果(还有许多其他结果):
***第一个e第一个f第一个x
*一个月一次

脚注

*#1:从技术上讲,有些情况下不需要指定它们。但即使这样,最好指定它们以消除任何可能的混淆:

  • 不带参数的函数:
function_from_dll.argtypes = ()
  • 返回 void 的函数:
function_from_dll.restype = None

*#2UndefinedBehavior[Wikipedia]: Undefined behavior)顾名思义,是一段代码的结果不能被“预测”(或由 CC++)标准保证)的情况。

  • 按预期工作
  • 未按预期工作
  • 有一些有趣的输出/副作用
  • 崩溃

它的“美妙之处”在于,有时候它看起来完全是随机的,有时候它“只在某些特定的情况下重现”(不同的机器,不同的 OSes,不同的环境,......)。底线是所有这些都是纯粹的巧合!问题在于代码(可以是当前代码(最高可能性)或它使用的其他代码(库,编译器))。

相关问题