小编典典

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

python

我用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。我是否犯了一个明显的错误,导致该函数计算错误?


阅读 336

收藏
2020-12-20

共1个答案

小编典典

清单[Python 3.Docs]:ctypes-Python的外部函数库。

为了在调用函数(驻留在.dll(.so)中)时正确转换所有内容(Python <=> C),需要指定2项内容(不包括x86调用约定(Win)):

参数类型
返回类型
在CTypes中,这可以通过指定:

argtypes-包含每个参数( CTypes)类型的列表(实际上是一个序列)(按它们在函数头中出现的顺序)
restype -单ctypes的类型
旁注:上述方法的替代方法是对外部函数(CFUNCTYPE,WINFUNCTYPE,PYFUNCTYPE)进行原型设计-检查Function prototypes部分(在开头的URL中)。

无论如何:

无法指定
拼写错误(基本上与上一个项目符号相同)
它们中的任何(如果需要(1) ),将导致默认被施加:所有被处理(C89风格)作为INT s,这(在大多数系统)是32位长。
这会生成未定义行为 (2)(在错误地指定它们时也适用),尤其是在64位 CPU / OS上,在后者中,较大类型的值(例如指针)可能会被截断。
显示的错误可能很多,有时甚至会引起误解。

您拼写了argtype(末尾缺少s)。

纠正它,你应该没事

范例:

对于由libdll00.dll(libdll00.so)导出的具有以下标头的函数func00:

double func00(uint32_t ui, float f, long long vll[8], void *pv, char *pc);

在Python的等效是:

func00 = libdll00.func00
func00.argtypes = [ctypes.c_uint32, ctypes.c_float, ctypes.c_longlong * 8, ctypes.c_void_p, ctypes.POINTER(ctypes.c_char)]
'''
It would be a lot easier (nicer, and in most cases recommended)
  the last element to be ctypes.c_char_p,
  but I chose this form to illustrate pointers in general.
'''
func00.restype = ctypes.c_double

相同(或非常相似)场景(还有很多其他场景)的一些(更具破坏性的)结果:

[SO]:Python ctypes cdll.LoadLibrary,实例化一个对象,执行其方法,私有变量地址被截断(@CristiFati的回答)
[SO]:不同操作系统上的python ctypes问题(@CristiFati的回答)
脚注
#1:从技术上讲,在某些情况下不需要指定它们。但是即使那样,最好还是指定它们,以消除任何可能的混淆:

没有参数的函数:

  function_from_dll.argtypes = []

函数返回void:

  function_from_dll.restype = None

#2:顾名思义,未定义的行为( [Wikipedia]:未定义的行为)是无法“预测”(或保证)一段代码的结果的情况。主要情况:

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

它的“美”在于有时似乎是完全随机的,有时它仅在某些特定情况(不同的机器,不同的OS,不同的环境……)下“复制” 。最重要的是,所有这些纯属巧合!问题出在代码(可能是当前代码(最高机率)或它使用的其他代码(库,编译器))上。

2020-12-20