【问题标题】:ctype why specify argtypesctype 为什么要指定 argtypes
【发布时间】:2014-06-24 03:22:36
【问题描述】:

我想用python调用c++库。

我的 C++ 库代码:

#include <stdio.h>

extern "C" {

int test_i(int i)
{
    return i+1;
}

}

我的python代码:

from ctypes import *
libc = cdll.LoadLibrary("cpp/libtest.so")
libc.test_f.argtypes = [c_int] # still works when comment
print libc.test_i(1)

我的问题是:如果我没有指定 argtypes,它仍然有效!调用C/C++函数时必须指定argtypes吗?

【问题讨论】:

标签: python c++ ctypes


【解决方案1】:

是的,你应该这样做,因为它起作用的原因是 ctypes 的猜测。将int test_i(int i) 替换为int test_i(char i),你会得到堆栈损坏——因为Python 端给函数4 或8 个字节,而C 端只读取1 个字节。根据平台的不同,这可能会在一段时间内被忽视,或者逐渐耗尽你的堆栈,或者立即崩溃,或者什么都不会发生——无论如何,这种代码风格有异味。

ctypes 进行基本的类型转换,但在很多情况下它不会有帮助,因为 Python 类型系统比 C 的简单得多。例如,Python 端无法确定您传递的是 1 字节、2 字节、4 字节还是 8 字节整数,因为 Python 整数没有大小。 Python 只有双精度浮点数,而 C 端也有单精度和半精度,并且可能还具有特定于平台的精度。字符串和字节仅在 Python 端正确分离,而在 C 端使用 Unicode 通常是一团糟。目前尚不清楚您是想使用wchar_t[] 还是char[],您想如何终止您的字符串,您是否需要它,以及您想如何解决Python 字符串不变性。如果没有正确的argtypes,您将无法轻松传递结构和数组。

另外,别忘了注明restype

libc.test_f.restype = c_int

默认情况下ctypes 假定所有函数都返回int,无论它适用于您的平台。根据调用约定,可能会使用 CPU 寄存器来返回结果——在这种情况下,您不会受到伤害,因为寄存器是否被读取都没有区别。在其他情况下,结果是通过堆栈传递的,而你搞砸了,因为没有消耗足够的堆栈(以及过度消耗)会导致堆栈损坏,函数的RET 将弹出错误的地址并在最好的情况下向 SIGSEGV 打招呼。在最坏的情况下,黑客会使用您的应用程序进行欺骗。

一般来说,argtypesrestype 是你的朋友,因为它们可以保护你的代码不会以不可预知的方式分崩离析,而且它们之所以需要,是因为 C 和 Python 中的一切都大相径庭。如果不是这样,没有人会因为 Zen 和电池而简单地强迫你使用这些。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-30
    • 2013-02-17
    • 2023-04-02
    • 1970-01-01
    • 2018-02-09
    • 2016-12-10
    相关资源
    最近更新 更多