新聞中心
ctypes —- python 的外部函數(shù)庫
ctypes 是 Python 的外部函數(shù)庫。它提供了與 C 兼容的數(shù)據(jù)類型,并允許調(diào)用 DLL 或共享庫中的函數(shù)。可使用該模塊以純 Python 形式對這些庫進(jìn)行封裝。

目前創(chuàng)新互聯(lián)公司已為1000+的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)絡(luò)空間、網(wǎng)站運(yùn)營、企業(yè)網(wǎng)站設(shè)計(jì)、忻州網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。
ctypes 教程
注:本教程中的示例代碼使用 doctest 來保證它們能正確運(yùn)行。 由于有些代碼示例在 Linux, Windows 或 macOS 上的行為有所不同,它們在注釋中包含了一些 doctest 指令。
注意:部分示例代碼引用了 ctypes c_int 類型。在 sizeof(long) == sizeof(int) 的平臺上此類型是 c_long 的一個(gè)別名。所以,在程序輸出 c_long 而不是你期望的 c_int 時(shí)不必感到迷惑 —- 它們實(shí)際上是同一種類型。
載入動態(tài)連接庫
ctypes 導(dǎo)出了 cdll 對象,在 Windows 系統(tǒng)中還導(dǎo)出了 windll 和 oledll 對象用于載入動態(tài)連接庫。
通過操作這些對象的屬性,你可以載入外部的動態(tài)鏈接庫。cdll 載入按標(biāo)準(zhǔn)的 cdecl 調(diào)用協(xié)議導(dǎo)出的函數(shù),而 windll 導(dǎo)入的庫按 stdcall 調(diào)用協(xié)議調(diào)用其中的函數(shù)。 oledll 也按 stdcall 調(diào)用協(xié)議調(diào)用其中的函數(shù),并假定該函數(shù)返回的是 Windows HRESULT 錯(cuò)誤代碼,并當(dāng)函數(shù)調(diào)用失敗時(shí),自動根據(jù)該代碼甩出一個(gè) OSError 異常。
在 3.3 版更改: 原來在 Windows 下拋出的異常類型 WindowsError 現(xiàn)在是 OSError 的一個(gè)別名。
這是一些 Windows 下的例子。注意:msvcrt 是微軟 C 標(biāo)準(zhǔn)庫,包含了大部分 C 標(biāo)準(zhǔn)函數(shù),這些函數(shù)都是以 cdecl 調(diào)用協(xié)議進(jìn)行調(diào)用的。
>>> from ctypes import *>>> print(windll.kernel32)>>> print(cdll.msvcrt)>>> libc = cdll.msvcrt>>>
Windows 會自動添加通常的 .dll 文件擴(kuò)展名。
備注
通過 cdll.msvcrt 調(diào)用的標(biāo)準(zhǔn) C 函數(shù),可能會導(dǎo)致調(diào)用一個(gè)過時(shí)的,與當(dāng)前 Python 所不兼容的函數(shù)。因此,請盡量使用標(biāo)準(zhǔn)的 Python 函數(shù),而不要使用 msvcrt 模塊。
在 Linux 下,必須使用 包含 文件擴(kuò)展名的文件名來導(dǎo)入共享庫。因此不能簡單使用對象屬性的方式來導(dǎo)入庫。因此,你可以使用方法 LoadLibrary(),或構(gòu)造 CDLL 對象來導(dǎo)入庫。
>>> cdll.LoadLibrary("libc.so.6")>>> libc = CDLL("libc.so.6")>>> libc>>>
操作導(dǎo)入的動態(tài)鏈接庫中的函數(shù)
通過操作dll對象的屬性來操作這些函數(shù)。
>>> from ctypes import *>>> libc.printf<_FuncPtr object at 0x...>>>> print(windll.kernel32.GetModuleHandleA)<_FuncPtr object at 0x...>>>> print(windll.kernel32.MyOwnFunction)Traceback (most recent call last):File "", line 1, in File "ctypes.py", line 239, in __getattr__func = _StdcallFuncPtr(name, self)AttributeError: function 'MyOwnFunction' not found>>>
注意:Win32 系統(tǒng)的動態(tài)庫,比如 kernel32 和 user32,通常會同時(shí)導(dǎo)出同一個(gè)函數(shù)的 ANSI 版本和 UNICODE 版本。UNICODE 版本通常會在名字最后以 W 結(jié)尾,而 ANSI 版本的則以 A 結(jié)尾。 win32的 GetModuleHandle 函數(shù)會根據(jù)一個(gè)模塊名返回一個(gè) 模塊句柄,該函數(shù)暨同時(shí)包含這樣的兩個(gè)版本的原型函數(shù),并通過宏 UNICODE 是否定義,來決定宏 GetModuleHandle 導(dǎo)出的是哪個(gè)具體函數(shù)。
/* ANSI version */HMODULE GetModuleHandleA(LPCSTR lpModuleName);/* UNICODE version */HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll 不會通過這樣的魔法手段來幫你決定選擇哪一種函數(shù),你必須顯式的調(diào)用 GetModuleHandleA 或 GetModuleHandleW,并分別使用字節(jié)對象或字符串對象作參數(shù)。
有時(shí)候,dlls的導(dǎo)出的函數(shù)名不符合 Python 的標(biāo)識符規(guī)范,比如 "??2@YAPAXI@Z"。此時(shí),你必須使用 getattr() 方法來獲得該函數(shù)。
>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")<_FuncPtr object at 0x...>>>>
Windows 下,有些 dll 導(dǎo)出的函數(shù)沒有函數(shù)名,而是通過其順序號調(diào)用。對此類函數(shù),你也可以通過 dll 對象的數(shù)值索引來操作這些函數(shù)。
>>> cdll.kernel32[1]<_FuncPtr object at 0x...>>>> cdll.kernel32[0]Traceback (most recent call last):File "", line 1, in File "ctypes.py", line 310, in __getitem__func = _StdcallFuncPtr(name, self)AttributeError: function ordinal 0 not found>>>
調(diào)用函數(shù)
你可以貌似是調(diào)用其它 Python 函數(shù)那樣直接調(diào)用這些函數(shù)。在這個(gè)例子中,我們調(diào)用了 time() 函數(shù),該函數(shù)返回一個(gè)系統(tǒng)時(shí)間戳(從 Unix 時(shí)間起點(diǎn)到現(xiàn)在的秒數(shù)),而``GetModuleHandleA()`` 函數(shù)返回一個(gè) win32 模塊句柄。
此函數(shù)中調(diào)用的兩個(gè)函數(shù)都使用了空指針(用 None 作為空指針):
>>> print(libc.time(None))1150640792>>> print(hex(windll.kernel32.GetModuleHandleA(None)))0x1d000000>>>
如果你用 cdecl 調(diào)用方式調(diào)用 stdcall 約定的函數(shù),則會甩出一個(gè)異常 ValueError。反之亦然。
>>> cdll.kernel32.GetModuleHandleA(None)Traceback (most recent call last):File "", line 1, in ValueError: Procedure probably called with not enough arguments (4 bytes missing)>>>>>> windll.msvcrt.printf(b"spam")Traceback (most recent call last):File "", line 1, in ValueError: Procedure probably called with too many arguments (4 bytes in excess)>>>
你必須閱讀這些庫的頭文件或說明文檔來確定它們的正確的調(diào)用協(xié)議。
在 Windows 中,ctypes 使用 win32 結(jié)構(gòu)化異常處理來防止由于在調(diào)用函數(shù)時(shí)使用非法參數(shù)導(dǎo)致的程序崩潰。
>>> windll.kernel32.GetModuleHandleA(32)Traceback (most recent call last):File "", line 1, in OSError: exception: access violation reading 0x00000020>>>
然而,總有許多辦法,通過調(diào)用 ctypes 使得 Python 程序崩潰。因此,你必須小心使用。 faulthandler 模塊可以用于幫助診斷程序崩潰的原因。(比如由于錯(cuò)誤的C庫函數(shù)調(diào)用導(dǎo)致的段錯(cuò)誤)。
None, integers, bytes objects and (unicode) strings are the only native Python objects that can directly be used as parameters in these function calls. None is passed as a C NULL pointer, bytes objects and strings are passed as pointer to the memory block that contains their data (char* or wchar_t*). Python integers are passed as the platforms default C int type, their value is masked to fit into the C type.
在我們開始調(diào)用函數(shù)前,我們必須先了解作為函數(shù)參數(shù)的 ctypes 數(shù)據(jù)類型。
基礎(chǔ)數(shù)據(jù)類型
ctypes 定義了一些和C兼容的基本數(shù)據(jù)類型:
|
ctypes 類型 |
C 類型 |
Python 類型 |
|---|---|---|
- 構(gòu)造函數(shù)接受任何具有真值的對象。
所有這些類型都可以通過使用正確類型和值的可選初始值調(diào)用它們來創(chuàng)建:
>>> c_int()c_long(0)>>> c_wchar_p("Hello, World")c_wchar_p(140018365411392)>>> c_ushort(-3)c_ushort(65533)>>>
由于這些類型是可變的,它們的值也可以在以后更改:
>>> i = c_int(42)>>> print(i)c_long(42)>>> print(i.value)42>>> i.value = -99>>> print(i.value)-99>>>
當(dāng)給指針類型的對象 c_char_p, c_wchar_p 和 c_void_p 等賦值時(shí),將改變它們所指向的 內(nèi)存地址,而 不是 它們所指向的內(nèi)存區(qū)域的 內(nèi)容 (這是理所當(dāng)然的,因?yàn)?Python 的 bytes 對象是不可變的):
>>> s = "Hello, World">>> c_s = c_wchar_p(s)>>> print(c_s)c_wchar_p(139966785747344)>>> print(c_s.value)Hello World>>> c_s.value = "Hi, there">>> print(c_s) # the memory location has changedc_wchar_p(139966783348904)>>> print(c_s.value)Hi, there>>> print(s) # first object is unchangedHello, World>>>
但你要注意不能將它們傳遞給會改變指針?biāo)竷?nèi)存的函數(shù)。如果你需要可改變的內(nèi)存塊,ctypes 提供了 create_string_buffer() 函數(shù),它提供多種方式創(chuàng)建這種內(nèi)存塊。當(dāng)前的內(nèi)存塊內(nèi)容可以通過 raw 屬性存取,如果你希望將它作為NUL結(jié)束的字符串,請使用 value 屬性:
>>> from ctypes import *>>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes>>> print(sizeof(p), repr(p.raw))3 b'\x00\x00\x00'>>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string>>> print(sizeof(p), repr(p.raw))6 b'Hello\x00'>>> print(repr(p.value))b'Hello'>>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer>>> print(sizeof(p), repr(p.raw))10 b'Hello\x00\x00\x00\x00\x00'>>> p.value = b"Hi">>> print(sizeof(p), repr(p.raw))10 b'Hi\x00lo\x00\x00\x00\x00\x00'>>>
The create_string_buffer() function replaces the old c_buffer() function (which is still available as an alias). To create a mutable memory block containing unicode characters of the C type wchar_t, use the create_unicode_buffer() function.
調(diào)用函數(shù),繼續(xù)
注意 printf 將打印到真正標(biāo)準(zhǔn)輸出設(shè)備,而*不是* sys.stdout,因此這些實(shí)例只能在控制臺提示符下工作,而不能在 IDLE 或 PythonWin 中運(yùn)行。
>>> printf = libc.printf>>> printf(b"Hello, %s\n", b"World!")Hello, World!14>>> printf(b"Hello, %S\n", "World!")Hello, World!14>>> printf(b"%d bottles of beer\n", 42)42 bottles of beer19>>> printf(b"%f bottles of beer\n", 42.5)Traceback (most recent call last):File "", line 1, in ArgumentError: argument 2: TypeError: Don't know how to convert parameter 2>>>
正如前面所提到過的,除了整數(shù)、字符串以及字節(jié)串之外,所有的 Python 類型都必須使用它們對應(yīng)的 ctypes 類型包裝,才能夠被正確地轉(zhuǎn)換為所需的C語言類型。
>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))An int 1234, a double 3.14000031>>>
使用自定義的數(shù)據(jù)類型調(diào)用函數(shù)
你也可以通過自定義 ctypes 參數(shù)轉(zhuǎn)換方式來允許自定義類型作為參數(shù)。 ctypes 會尋找 _as_parameter_ 屬性并使用它作為函數(shù)參數(shù)。當(dāng)然,它必須是數(shù)字、字符串或者二進(jìn)制字符串:
>>> class Bottles:... def __init__(self, number):... self._as_parameter_ = number...>>> bottles = Bottles(42)>>> printf(b"%d bottles of beer\n", bottles)42 bottles of beer19>>>
如果你不想把實(shí)例的數(shù)據(jù)存儲到 _as_parameter_ 屬性??梢酝ㄟ^定義 property 函數(shù)計(jì)算出這個(gè)屬性。
指定必選參數(shù)的類型(函數(shù)原型)
可以通過設(shè)置 argtypes 屬性的方法指定從 DLL 中導(dǎo)出函數(shù)的必選參數(shù)類型。
argtypes 必須是一個(gè) C 數(shù)據(jù)類型的序列 (這里的 printf 可能不是個(gè)好例子,因?yàn)樗亲冮L參數(shù),而且每個(gè)參數(shù)的類型依賴于格式化字符串,不過嘗試這個(gè)功能也很方便):
>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]>>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)String 'Hi', Int 10, Double 2.20000037>>>
指定數(shù)據(jù)類型可以防止不合理的參數(shù)傳遞(就像 C 函數(shù)的原型),并且會自動嘗試將參數(shù)轉(zhuǎn)換為需要的類型:
>>> printf(b"%d %d %d", 1, 2, 3)Traceback (most recent call last):File "", line 1, in ArgumentError: argument 2: TypeError: wrong type>>> printf(b"%s %d %f\n", b"X", 2, 3)X 2 3.00000013>>>
如果你想通過自定義類型傳遞參數(shù)給函數(shù),必須實(shí)現(xiàn) from_param() 類方法,才能夠?qū)⒋俗远x類型用于 argtypes 序列。from_param() 類方法接受一個(gè) Python 對象作為函數(shù)輸入,它應(yīng)該進(jìn)行類型檢查或者其他必要的操作以保證接收到的對象是合法的,然后返回這個(gè)對象,或者它的 _as_parameter_ 屬性,或者其他你想要傳遞給 C 函數(shù)的參數(shù)。這里也一樣,返回的結(jié)果必須是整型、字符串、二進(jìn)制字符串、 ctypes 類型,或者一個(gè)具有 _as_parameter_ 屬性的對象。
返回類型
By default functions are assumed to return the C int type. Other return types can be specified by setting the restype attribute of the function object.
這是個(gè)更高級的例子,它調(diào)用了 strchr 函數(shù),這個(gè)函數(shù)接收一個(gè)字符串指針以及一個(gè)字符作為參數(shù),返回另一個(gè)字符串指針。
>>> strchr = libc.strchr>>> strchr(b"abcdef", ord("d"))8059983>>> strchr.restype = c_char_p # c_char_p is a pointer to a string>>> strchr(b"abcdef", ord("d"))b'def'>>> print(strchr(b"abcdef", ord("x")))None>>>
如果希望避免上述的 ord("x") 調(diào)用,可以設(shè)置 argtypes 屬性,第二個(gè)參數(shù)就會將單字符的 Python 二進(jìn)制字符對象轉(zhuǎn)換為 C 字符:
>>> strchr.restype = c_char_p>>> strchr.argtypes = [c_char_p, c_char]>>> strchr(b"abcdef", b"d")'def'>>> strchr(b"abcdef", b"def")Traceback (most recent call last):File "", line 1, in ArgumentError: argument 2: TypeError: one character string expected>>> print(strchr(b"abcdef", b"x"))None>>> strchr(b"abcdef", b"d")'def'>>>
如果外部函數(shù)返回了一個(gè)整數(shù),你也可以使用要給可調(diào)用的 Python 對象(比如函數(shù)或者類)作為 restype 屬性的值。將會以 C 函數(shù)返回的 整數(shù) 對象作為參數(shù)調(diào)用這個(gè)可調(diào)用對象,執(zhí)行后的結(jié)果作為最終函數(shù)返回值。這在錯(cuò)誤返回值校驗(yàn)和自動拋出異常等方面比較有用。
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA>>> def ValidHandle(value):... if value == 0:... raise WinError()... return value...>>>>>> GetModuleHandle.restype = ValidHandle>>> GetModuleHandle(None)486539264>>> GetModuleHandle("something silly")Traceback (most recent call last):File "", line 1, in File "", line 3, in ValidHandle OSError: [Errno 126] The specified module could not be found.>>>
WinError 函數(shù)可以調(diào)用 Windows 的 FormatMessage() API 獲取錯(cuò)誤碼的字符串說明,然后 返回 一個(gè)異常。 WinError 接收一個(gè)可選的錯(cuò)誤碼作為參數(shù),如果沒有的話,它將調(diào)用 GetLastError() 獲取錯(cuò)誤碼。
請注意,使用 errcheck 屬性可以實(shí)現(xiàn)更強(qiáng)大的錯(cuò)誤檢查手段;詳情請見參考手冊。
傳遞指針(或以引用方式傳遞形參)
有時(shí)候 C 函數(shù)接口可能由于要往某個(gè)地址寫入值,或者數(shù)據(jù)太大不適合作為值傳遞,從而希望接收一個(gè) 指針 作為數(shù)據(jù)參數(shù)類型。這和 傳遞參數(shù)引用 類似。
ctypes 暴露了 byref() 函數(shù)用于通過引用傳遞參數(shù),使用 pointer() 函數(shù)也能達(dá)到同樣的效果,只不過 pointer() 需要更多步驟,因?yàn)樗葮?gòu)造一個(gè)真實(shí)指針對象。所以在 Python 代碼本身不需要使用這個(gè)指針對象的情況下,使用 byref() 效率更高。
>>> i = c_int()>>> f = c_float()>>> s = create_string_buffer(b'\000' * 32)>>> print(i.value, f.value, repr(s.value))0 0.0 b''>>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",... byref(i), byref(f), s)3>>> print(i.value, f.value, repr(s.value))1 3.1400001049 b'Hello'>>>
結(jié)構(gòu)體和聯(lián)合
結(jié)構(gòu)體和聯(lián)合必須繼承自 ctypes 模塊中的 Structure 和 Union 。子類必須定義 _fields_ 屬性。 _fields_ 是一個(gè)二元組列表,二元組中包含 field name 和 field type 。
type 字段必須是一個(gè) ctypes 類型,比如 c_int,或者其他 ctypes 類型: 結(jié)構(gòu)體、聯(lián)合、數(shù)組、指針。
這是一個(gè)簡單的 POINT 結(jié)構(gòu)體,它包含名稱為 x 和 y 的兩個(gè)變量,還展示了如何通過構(gòu)造函數(shù)初始化結(jié)構(gòu)體。
>>> from ctypes import *>>> class POINT(Structure):... _fields_ = [("x", c_int),... ("y", c_int)]...>>> point = POINT(10, 20)>>> print(point.x, point.y)10 20>>> point = POINT(y=5)>>> print(point.x, point.y)0 5>>> POINT(1, 2, 3)Traceback (most recent call last):File "", line 1, in TypeError: too many initializers>>>
當(dāng)然,你可以構(gòu)造更復(fù)雜的結(jié)構(gòu)體。一個(gè)結(jié)構(gòu)體可以通過設(shè)置 type 字段包含其他結(jié)構(gòu)體或者自身。
這是以一個(gè) RECT 結(jié)構(gòu)體,他包含了兩個(gè) POINT ,分別叫 upperleft 和 lowerright:
>>> class RECT(Structure):... _fields_ = [("upperleft", POINT),... ("lowerright", POINT)]...>>> rc = RECT(point)>>> print(rc.upperleft.x, rc.upperleft.y)0 5>>> print(rc.lowerright.x, rc.lowerright.y)0 0>>>
嵌套結(jié)構(gòu)體可以通過幾種方式構(gòu)造初始化:
>>> r = RECT(POINT(1, 2), POINT(3, 4))>>> r = RECT((1, 2), (3, 4))
可以通過 類 獲取字段 descriptor ,它能提供很多有用的調(diào)試信息。
>>> print(POINT.x)>>> print(POINT.y)>>>
警告
ctypes 不支持帶位域的結(jié)構(gòu)體、聯(lián)合以值的方式傳給函數(shù)。這可能在 32 位 x86 平臺上可以正常工作,但是對于一般情況,這種行為是未定義的。帶位域的結(jié)構(gòu)體、聯(lián)合應(yīng)該總是通過指針傳遞給函數(shù)。
結(jié)構(gòu)體/聯(lián)合字段對齊及字節(jié)順序
默認(rèn)情況下,結(jié)構(gòu)體和聯(lián)合的字段與 C 的字節(jié)對齊是一樣的。也可以在定義子類的時(shí)候指定類的 _pack_ 屬性來覆蓋這種行為。 它必須設(shè)置為一個(gè)正整數(shù),表示字段的最大對齊字節(jié)。這和 MSVC 中的 #pragma pack(n) 功能一樣。
ctypes 中的結(jié)構(gòu)體和聯(lián)合使用的是本地字節(jié)序。要使用非本地字節(jié)序,可以使用 BigEndianStructure, LittleEndianStructure, BigEndianUnion, and LittleEndianUnion 作為基類。這些類不能包含指針字段。
結(jié)構(gòu)體和聯(lián)合中的位域
結(jié)構(gòu)體和聯(lián)合中是可以包含位域字段的。位域只能用于整型字段,位長度通過 _fields_ 中的第三個(gè)參數(shù)指定:
>>> class Int(Structure):... _fields_ = [("first_16", c_int, 16),... ("second_16", c_int, 16)]...>>> print(Int.first_16)>>> print(Int.second_16)>>>
數(shù)組
數(shù)組是一個(gè)序列,包含指定個(gè)數(shù)元素,且必須類型相同。
創(chuàng)建數(shù)組類型的推薦方式是使用一個(gè)類型乘以一個(gè)正數(shù):
TenPointsArrayType = POINT * 10
下面是一個(gè)構(gòu)造的數(shù)據(jù)案例,結(jié)構(gòu)體中包含了4個(gè) POINT 和一些其他東西。
>>> from ctypes import *>>> class POINT(Structure):... _fields_ = ("x", c_int), ("y", c_int)...>>> class MyStruct(Structure):... _fields_ = [("a", c_int),... ("b", c_float),... ("point_array", POINT * 4)]>>>>>> print(len(MyStruct().point_array))4>>>
和平常一樣,通過調(diào)用它創(chuàng)建實(shí)例:
arr = TenPointsArrayType()for pt in arr:print(pt.x, pt.y)
以上代碼會打印幾行 0 0 ,因?yàn)閿?shù)組內(nèi)容被初始化為 0.
也能通過指定正確類型的數(shù)據(jù)來初始化:
>>> from ctypes import *>>> TenIntegers = c_int * 10>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)>>> print(ii)>>> for i in ii: print(i, end=" ")...1 2 3 4 5 6 7 8 9 10>>>
指針
可以將 ctypes 類型數(shù)據(jù)傳入 pointer() 函數(shù)創(chuàng)建指針:
>>> from ctypes import *>>> i = c_int(42)>>> pi = pointer(i)>>>
指針實(shí)例擁有 contents 屬性,它返回指針指向的真實(shí)對象,如上面的 i 對象:
>>> pi.contentsc_long(42)>>>
注意 ctypes 并沒有 OOR (返回原始對象), 每次訪問這個(gè)屬性時(shí)都會構(gòu)造返回一個(gè)新的相同對象:
>>> pi.contents is iFalse>>> pi.contents is pi.contentsFalse>>>
將這個(gè)指針的 contents 屬性賦值為另一個(gè) c_int 實(shí)例將會導(dǎo)致該指針指向該實(shí)例的內(nèi)存地址:
>>> i = c_int(99)>>> pi.contents = i>>> pi.contentsc_long(99)>>>
指針對象也可以通過整數(shù)下標(biāo)進(jìn)行訪問:
>>> pi[0]99>>>
通過整數(shù)下標(biāo)賦值可以改變指針?biāo)赶虻恼鎸?shí)內(nèi)容:
>>> print(i)c_long(99)>>> pi[0] = 22>>> print(i)c_long(22)>>>
使用 0 以外的索引也是合法的,但是你必須確保知道自己為什么這么做,就像 C 語言中: 你可以訪問或者修改任意內(nèi)存內(nèi)容。 通常只會在函數(shù)接收指針是才會使用這種特性,而且你 知道 這個(gè)指針指向的是一個(gè)數(shù)組而不是單個(gè)值。
內(nèi)部細(xì)節(jié), pointer() 函數(shù)不只是創(chuàng)建了一個(gè)指針實(shí)例,它首先創(chuàng)建了一個(gè)指針 類型 。這是通過調(diào)用 POINTER() 函數(shù)實(shí)現(xiàn)的,它接收 ctypes 類型為參數(shù),返回一個(gè)新的類型:
>>> PI = POINTER(c_int)>>> PI>>> PI(42)Traceback (most recent call last):File "", line 1, in TypeError: expected c_long instead of int>>> PI(c_int(42))>>>
無參調(diào)用指針類型可以創(chuàng)建一個(gè) NULL 指針。 NULL 指針的布爾值是 False
>>> null_ptr = POINTER(c_int)()>>> print(bool(null_ptr))False>>>
解引用指針的時(shí)候, ctypes 會幫你檢測是否指針為 NULL (但是解引用無效的 非 NULL 指針仍會導(dǎo)致 Python 崩潰):
>>> null_ptr[0]Traceback (most recent call last):....ValueError: NULL pointer access>>>>>> null_ptr[0] = 1234Traceback (most recent call last):....ValueError: NULL pointer access>>>
類型轉(zhuǎn)換
通常情況下, ctypes 具有嚴(yán)格的類型檢查。這代表著, 如果在函數(shù) argtypes
文章題目:創(chuàng)新互聯(lián)Python教程:ctypes —- Python 的外部函數(shù)庫
當(dāng)前鏈接:http://fisionsoft.com.cn/article/dpccije.html


咨詢
建站咨詢
