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

时间:2019-10-29 15:15:53

标签: python c ctypes

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

1 个答案:

答案 0 :(得分:2)

列出[Python 3.Docs]: ctypes - A foreign function library for Python

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

  1. 参数类型
  2. 返回类型

CTypes 中,这可以通过指定:

  1. argtypes -包含每个参数( CTypes )类型的列表(按它们在函数头中的显示顺序)
  2. restype -单个 CTypes 类型


  • 无法指定
  • 拼写错误(基本上,与以前的项目符号相同)

其中的任何一个(必要时为 1 )将导致应用默认设置:所有内容均已处理( C89 样式)为 int (在大多数系统上) 32 位长。
这会生成未定义行为 (2)
(当错误指定错误代码时也适用)。

您拼错了 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

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


脚语

  • #1。

    从技术上讲,在某些情况下不需要指定它们。但是即使如此,最好还是指定它们,以消除任何可能的混淆:

    • 不带参数的函数:

      function_from_dll.argtypes = []
      
    • 返回 void 的函数:

      function_from_dll.restype = None
      
  • #2。

    顾名思义,

    未定义行为[Wikipedia]: Undefined behavior)是一种情况,其中一段代码的结果无法“预测”(或保证)。主要情况:

    • 按预期工作
    • 无法正常工作
      • 具有一些有趣的输出/副作用
      • 崩溃

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