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

查看:46
本文介绍了通过 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

I created the shared file using the command: $ gcc -fPIC -shared -o test.so test.c

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

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?

推荐答案

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

为了在调用函数时正确转换所有内容(Python <=> C)(驻留在 .dll (.so)),需要指定两件事(除了 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中,这是通过指定:

  1. argtypes - 包含每个参数 (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

旁注:上述的替代方法是对外部函数进行原型设计(CFUNCTYPEWINFUNCTYPEPYFUNCTYPE - 检查 函数原型 部分(在开头的 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 (which basically is same thing as previous bullet)

任何一个(如果需要(1)),将导致应用默认值:所有都被处理(C89style) 作为 int s,其中(在大多数系统上)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

示例:

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

For a function func exported by libdll.dll (libdll.so) with the following header:

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

Python 等价物是:

func = libdll.func
func.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.
'''
func.restype = ctypes.c_double

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

  • #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:未定义行为([维基百科]:未定义行为) 顾名思义,是一段代码的结果无法预测"的情况.(或保证).主要案例:

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

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


      美"其中之一是有时它看起来完全随机,有时它只是复制"在某些特定情况下(不同的机器、不同的操作系统、不同的环境,......).最重要的是,所有这些纯属巧合!问题出在代码(可以是当前代码(可能性最大)或它使用的其他代码(库、编译器)).


      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天全站免登陆