我想用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
吗?
答案 0 :(得分:6)
是的,你应该,因为它有效的原因是因为ctypes&#39;猜测。将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打招呼最好的情况。在最糟糕的情况下,黑客会使用您的应用程序进行欺骗。
通常,argtypes
和restype
是您的朋友,因为它们可以保护您的代码免于以不可预测的方式分崩离析,并且只需要因为C和Python中的所有内容都有所不同而需要它们。如果不是这样,由于禅和电池,没有人会强迫你永远使用这些。