通过ctypes从Python调用的C函数返回不正确的值 [英] C function called from Python via ctypes returns incorrect value

查看:371
本文介绍了通过ctypes从Python调用的C函数返回不正确的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我用C语言编写了一个简单的函数,可以将给定的数字提高到给定的幂。当我在C中调用该函数时,该函数返回正确的值,但是当我在Python中调用该函数时,它返回一个不同的,错误的值。

I have written a simple function in C that raises a given number to a given power. The function returns the correct value when I call it in C, but when I call it in Python it returns a different, incorrect value.

命令:
$ gcc -fPIC -shared -o test.so test.c

我有尝试了C函数的不同配置,其中一些返回期望值,而有些则没有。例如,当我的函数使用返回x * x 的简单正方形而没有 for 循环时,它返回了正确的

I have tried different configurations of the C function, some of which return the expected value and some of which do not. For example, when my function a simple square using return x*x, without a for loop, it returned the correct value in Python.

我希望最终能够在python中调用C函数,该函数将返回二维C数组。

I hope to eventually be able to call a C function in python that will return a two-dimensional C array.

#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。

I obtain the expected output of 125.0 when I call the function in C, but when I call the function in python, it returns a value of 0.0.

这是我第一次使用ctypes。我是否犯了一个明显的错误,导致该函数计算错误?

This is my first time using ctypes. Have I made an obvious error that is causing the function to miscalculate?

推荐答案

列出 [Python 3.Docs]:ctypes-Python的外部函数库

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

In order for everything to be properly converted (Python <=> C) when calling the function (residing in a .dll (.so)), 2 things need to be specified (leaving x86 calling convention (Win) aside):


  1. 参数类型

  2. 返回类型

CTypes ,这可以通过指定以下内容来实现:

In CTypes, this is achieved by specifying:


  1. argtypes -a包含每个参数( CTypes )类型的列表(实际上是一个序列)(以它们在函数头中出现的顺序)

  2. restype -单个 CTypes 类型

  1. argtypes - a list (actually a sequence) containing each argument (CTypes) type (in the order they appear in the function header)
  2. restype - a single CTypes type

旁注:上面的替代方法是对外部函数( CFUNCTYPE WINFUNCTYPE PYFUNCTYPE, -检查 功能原型 部分(在开头的 URL 中)。

Side note: an alternative to the above, is prototyping foreign functions (CFUNCTYPE, WINFUNCTYPE, PYFUNCTYPE - check the Function prototypes section (in the URL at the beginning)).

无论如何:


  • 无法指定

  • 拼写错误(基本上与上一个项目符号相同)

  • Failing to specify
  • Misspelling (basically, same thing as previous bullet)

其中任何一个 (在需要(1)的情况下),将导致应用默认设置:将所有内容( C89 样式)视为 int (在大多数系统上) 32 位长。
这会生成未定义行为 (2)
(也适用于错误指定它们的情况),尤其是在 64位上 CPU / OS ,其中较大类型的值(例如指针)可能会被截断。
显示的错误可能很多,有时甚至会引起误解。

any of them (where required (1)), will result in defaults being applied: all are treated (C89 style) as ints, which (on most systems) are 32 bits long.
This generates Undefined Behavior (2)
(also applies when incorrectly specifying them), especially on 64bit CPU / OS, where values of larger types (e.g. pointers) could be truncated.
The displayed errors can be numerous and sometimes misleading.

您拼错了 argtype (缺少 s )。

You misspelled argtype (lacking an s at the end).

更正这一点,您应该没事

Correct that, and you should be fine

示例

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

For a function func00 exported by libdll00.dll (libdll00.so) with the following header:

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, instantiate an object, execute its method, private variable address truncated (@CristiFati's answer)
  • [SO]: python ctypes issue on different OSes (@CristiFati's answer)

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

  • #1: Technically, there are situations where it's not required to specify them. But even then, it's best to have them specified in order to eliminate any possible confusion:


  • 不带参数的函数:

  • Function with no arguments:

  function_from_dll.argtypes = []



  • 函数返回 void

      function_from_dll.restype = None
    



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

    #2: Undefined Behavior ([Wikipedia]: Undefined behavior) as the name suggests, is a situation when the outcome of a piece of code can't be "predicted" (or guaranteed). The main cases:


    • 按预期工作

    • 无法按预期工作

      • 有一些有趣的输出/副作用

      • 崩溃


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


      The "beauty" of it is that sometimes it seems totally random, sometimes it "only reproduces" under some specific circumstances (different machines, different OSes, different environments, ...). Bottom line is that all of them are purely coincidental! The problem lies in the code (can be the current code (highest chances) or other code it uses (libraries, compilers)).

      这篇关于通过ctypes从Python调用的C函数返回不正确的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆