2014-06-24 55 views
0

我想用python調用C++庫。ctype爲什麼指定argtypes

我的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

是的,你應該,因爲它爲什麼工作是因爲ctypes的猜測。將int test_i(int i)替換爲int test_i(char i),您將得到堆棧損壞 - 因爲Python端提供了4或8字節的功能,而C端只能讀取1個字節。根據平臺的不同,這可能會被忽略一段時間,或者逐漸吃掉你的堆棧,或者立即崩潰,或者什麼都不會發生 - 無論如何,這種代碼風格會有異味。

​​做了基本的類型轉換,但它在很多情況下都沒有幫助,因爲Python類型系統比C更簡單。例如,由於Python整數沒有大小,Python方面無法確定是否傳遞1字節,2字節,4字節或8字節整數。 Python只有雙精度浮點數,而C端也有單精度浮點數和一半精度浮點數,並且可能還有平臺特定的精度。字符串和字節只在Python端分開,而在C端使用Unicode通常是一團糟。目前尚不清楚您是否想要使用wchar_t[]char[],您希望如何終止您的字符串,並且您是否需要此操作,以及您希望如何解決Python字符串不可變性問題。如果沒有正確的argtypes,則無法輕鬆傳遞結構和數組。

此外,也別忘指定restype

libc.test_f.restype = c_int 

默認情況下​​假定所有功能回到int,不管它是您的平臺。根據調用約定,CPU寄存器可能會用於返回結果 - 在這種情況下,您不會受到傷害,因爲寄存器是否被讀取沒有區別。在其他情況下,結果是通過堆棧傳遞的,並且由於沒有消耗足夠的堆棧(以及過度消耗)而導致堆棧損壞,函數的RET將彈出錯誤的地址並在最佳情況下向SIGSEGV打招呼。最糟糕的是,黑客會利用你的應用程序進行欺騙。

一般來說,argtypesrestype是你的朋友,因爲他們保護您的代碼在不可預知的方式分崩離析,而他們需要只是因爲一切是太多不同使用C和Python。如果不是,沒有人會因爲禪宗和電池而迫使你使用這些。