ctype为什么要指定argtypes

时间:2014-06-24 03:22:37

标签: python c++ ctypes

我想用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吗?

1 个答案:

答案 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打招呼最好的情况。在最糟糕的情况下,黑客会使用您的应用程序进行欺骗。

通常,argtypesrestype是您的朋友,因为它们可以保护您的代码免于以不可预测的方式分崩离析,并且只需要因为C和Python中的所有内容都有所不同而需要它们。如果不是这样,由于禅和电池,没有人会强迫你永远使用这些。