日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯網營銷解決方案
創(chuàng)新互聯Python教程:ctypes —- Python 的外部函數庫

ctypes —- python 的外部函數庫


ctypes 是 Python 的外部函數庫。它提供了與 C 兼容的數據類型,并允許調用 DLL 或共享庫中的函數??墒褂迷撃K以純 Python 形式對這些庫進行封裝。

目前創(chuàng)新互聯公司已為1000+的企業(yè)提供了網站建設、域名、網絡空間、網站運營、企業(yè)網站設計、忻州網站維護等服務,公司將堅持客戶導向、應用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協力一起成長,共同發(fā)展。

ctypes 教程

注:本教程中的示例代碼使用 doctest 來保證它們能正確運行。 由于有些代碼示例在 Linux, Windows 或 macOS 上的行為有所不同,它們在注釋中包含了一些 doctest 指令。

注意:部分示例代碼引用了 ctypes c_int 類型。在 sizeof(long) == sizeof(int) 的平臺上此類型是 c_long 的一個別名。所以,在程序輸出 c_long 而不是你期望的 c_int 時不必感到迷惑 —- 它們實際上是同一種類型。

載入動態(tài)連接庫

ctypes 導出了 cdll 對象,在 Windows 系統(tǒng)中還導出了 windlloledll 對象用于載入動態(tài)連接庫。

通過操作這些對象的屬性,你可以載入外部的動態(tài)鏈接庫。cdll 載入按標準的 cdecl 調用協議導出的函數,而 windll 導入的庫按 stdcall 調用協議調用其中的函數。 oledll 也按 stdcall 調用協議調用其中的函數,并假定該函數返回的是 Windows HRESULT 錯誤代碼,并當函數調用失敗時,自動根據該代碼甩出一個 OSError 異常。

在 3.3 版更改: 原來在 Windows 下拋出的異常類型 WindowsError 現在是 OSError 的一個別名。

這是一些 Windows 下的例子。注意:msvcrt 是微軟 C 標準庫,包含了大部分 C 標準函數,這些函數都是以 cdecl 調用協議進行調用的。

 
 
 
 
  1. >>> from ctypes import *
  2. >>> print(windll.kernel32)
  3. >>> print(cdll.msvcrt)
  4. >>> libc = cdll.msvcrt
  5. >>>

Windows 會自動添加通常的 .dll 文件擴展名。

備注

通過 cdll.msvcrt 調用的標準 C 函數,可能會導致調用一個過時的,與當前 Python 所不兼容的函數。因此,請盡量使用標準的 Python 函數,而不要使用 msvcrt 模塊。

在 Linux 下,必須使用 包含 文件擴展名的文件名來導入共享庫。因此不能簡單使用對象屬性的方式來導入庫。因此,你可以使用方法 LoadLibrary(),或構造 CDLL 對象來導入庫。

 
 
 
 
  1. >>> cdll.LoadLibrary("libc.so.6")
  2. >>> libc = CDLL("libc.so.6")
  3. >>> libc
  4. >>>

操作導入的動態(tài)鏈接庫中的函數

通過操作dll對象的屬性來操作這些函數。

 
 
 
 
  1. >>> from ctypes import *
  2. >>> libc.printf
  3. <_FuncPtr object at 0x...>
  4. >>> print(windll.kernel32.GetModuleHandleA)
  5. <_FuncPtr object at 0x...>
  6. >>> print(windll.kernel32.MyOwnFunction)
  7. Traceback (most recent call last):
  8. File "", line 1, in
  9. File "ctypes.py", line 239, in __getattr__
  10. func = _StdcallFuncPtr(name, self)
  11. AttributeError: function 'MyOwnFunction' not found
  12. >>>

注意:Win32 系統(tǒng)的動態(tài)庫,比如 kernel32user32,通常會同時導出同一個函數的 ANSI 版本和 UNICODE 版本。UNICODE 版本通常會在名字最后以 W 結尾,而 ANSI 版本的則以 A 結尾。 win32的 GetModuleHandle 函數會根據一個模塊名返回一個 模塊句柄,該函數暨同時包含這樣的兩個版本的原型函數,并通過宏 UNICODE 是否定義,來決定宏 GetModuleHandle 導出的是哪個具體函數。

 
 
 
 
  1. /* ANSI version */
  2. HMODULE GetModuleHandleA(LPCSTR lpModuleName);
  3. /* UNICODE version */
  4. HMODULE GetModuleHandleW(LPCWSTR lpModuleName);

windll 不會通過這樣的魔法手段來幫你決定選擇哪一種函數,你必須顯式的調用 GetModuleHandleAGetModuleHandleW,并分別使用字節(jié)對象或字符串對象作參數。

有時候,dlls的導出的函數名不符合 Python 的標識符規(guī)范,比如 "??2@YAPAXI@Z"。此時,你必須使用 getattr() 方法來獲得該函數。

 
 
 
 
  1. >>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")
  2. <_FuncPtr object at 0x...>
  3. >>>

Windows 下,有些 dll 導出的函數沒有函數名,而是通過其順序號調用。對此類函數,你也可以通過 dll 對象的數值索引來操作這些函數。

 
 
 
 
  1. >>> cdll.kernel32[1]
  2. <_FuncPtr object at 0x...>
  3. >>> cdll.kernel32[0]
  4. Traceback (most recent call last):
  5. File "", line 1, in
  6. File "ctypes.py", line 310, in __getitem__
  7. func = _StdcallFuncPtr(name, self)
  8. AttributeError: function ordinal 0 not found
  9. >>>

調用函數

你可以貌似是調用其它 Python 函數那樣直接調用這些函數。在這個例子中,我們調用了 time() 函數,該函數返回一個系統(tǒng)時間戳(從 Unix 時間起點到現在的秒數),而``GetModuleHandleA()`` 函數返回一個 win32 模塊句柄。

此函數中調用的兩個函數都使用了空指針(用 None 作為空指針):

 
 
 
 
  1. >>> print(libc.time(None))
  2. 1150640792
  3. >>> print(hex(windll.kernel32.GetModuleHandleA(None)))
  4. 0x1d000000
  5. >>>

如果你用 cdecl 調用方式調用 stdcall 約定的函數,則會甩出一個異常 ValueError。反之亦然。

 
 
 
 
  1. >>> cdll.kernel32.GetModuleHandleA(None)
  2. Traceback (most recent call last):
  3. File "", line 1, in
  4. ValueError: Procedure probably called with not enough arguments (4 bytes missing)
  5. >>>
  6. >>> windll.msvcrt.printf(b"spam")
  7. Traceback (most recent call last):
  8. File "", line 1, in
  9. ValueError: Procedure probably called with too many arguments (4 bytes in excess)
  10. >>>

你必須閱讀這些庫的頭文件或說明文檔來確定它們的正確的調用協議。

在 Windows 中,ctypes 使用 win32 結構化異常處理來防止由于在調用函數時使用非法參數導致的程序崩潰。

 
 
 
 
  1. >>> windll.kernel32.GetModuleHandleA(32)
  2. Traceback (most recent call last):
  3. File "", line 1, in
  4. OSError: exception: access violation reading 0x00000020
  5. >>>

然而,總有許多辦法,通過調用 ctypes 使得 Python 程序崩潰。因此,你必須小心使用。 faulthandler 模塊可以用于幫助診斷程序崩潰的原因。(比如由于錯誤的C庫函數調用導致的段錯誤)。

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.

在我們開始調用函數前,我們必須先了解作為函數參數的 ctypes 數據類型。

基礎數據類型

ctypes 定義了一些和C兼容的基本數據類型:

c_bool

_Bool

bool (1)

c_char

char

單字符字節(jié)串對象

c_wchar

wchar_t

單字符字符串

c_byte

char

int

c_ubyte

unsigned char

int

c_short

short

int

c_ushort

unsigned short

int

c_int

int

int

c_uint

unsigned int

int

c_long

long

int

c_ulong

unsigned long

int

c_longlong

int64 or long long

int

c_ulonglong

unsigned int64 or unsigned long long

int

c_size_t

size_t

int

c_ssize_t

ssize_t or Py_ssize_t

int

c_float

float

float

c_double

double

float

c_longdouble

long double

float

c_char_p

char (NUL terminated)

字節(jié)串對象或 None

c_wchar_p

wchar_t (NUL terminated)

字符串或 None

c_void_p

void*

int 或 None

ctypes 類型

C 類型

Python 類型

  1. 構造函數接受任何具有真值的對象。

所有這些類型都可以通過使用正確類型和值的可選初始值調用它們來創(chuàng)建:

 
 
 
 
  1. >>> c_int()
  2. c_long(0)
  3. >>> c_wchar_p("Hello, World")
  4. c_wchar_p(140018365411392)
  5. >>> c_ushort(-3)
  6. c_ushort(65533)
  7. >>>

由于這些類型是可變的,它們的值也可以在以后更改:

 
 
 
 
  1. >>> i = c_int(42)
  2. >>> print(i)
  3. c_long(42)
  4. >>> print(i.value)
  5. 42
  6. >>> i.value = -99
  7. >>> print(i.value)
  8. -99
  9. >>>

當給指針類型的對象 c_char_p, c_wchar_p 和 c_void_p 等賦值時,將改變它們所指向的 內存地址,而 不是 它們所指向的內存區(qū)域的 內容 (這是理所當然的,因為 Python 的 bytes 對象是不可變的):

 
 
 
 
  1. >>> s = "Hello, World"
  2. >>> c_s = c_wchar_p(s)
  3. >>> print(c_s)
  4. c_wchar_p(139966785747344)
  5. >>> print(c_s.value)
  6. Hello World
  7. >>> c_s.value = "Hi, there"
  8. >>> print(c_s) # the memory location has changed
  9. c_wchar_p(139966783348904)
  10. >>> print(c_s.value)
  11. Hi, there
  12. >>> print(s) # first object is unchanged
  13. Hello, World
  14. >>>

但你要注意不能將它們傳遞給會改變指針所指內存的函數。如果你需要可改變的內存塊,ctypes 提供了 create_string_buffer() 函數,它提供多種方式創(chuàng)建這種內存塊。當前的內存塊內容可以通過 raw 屬性存取,如果你希望將它作為NUL結束的字符串,請使用 value 屬性:

 
 
 
 
  1. >>> from ctypes import *
  2. >>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes
  3. >>> print(sizeof(p), repr(p.raw))
  4. 3 b'\x00\x00\x00'
  5. >>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string
  6. >>> print(sizeof(p), repr(p.raw))
  7. 6 b'Hello\x00'
  8. >>> print(repr(p.value))
  9. b'Hello'
  10. >>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
  11. >>> print(sizeof(p), repr(p.raw))
  12. 10 b'Hello\x00\x00\x00\x00\x00'
  13. >>> p.value = b"Hi"
  14. >>> print(sizeof(p), repr(p.raw))
  15. 10 b'Hi\x00lo\x00\x00\x00\x00\x00'
  16. >>>

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.

調用函數,繼續(xù)

注意 printf 將打印到真正標準輸出設備,而*不是* sys.stdout,因此這些實例只能在控制臺提示符下工作,而不能在 IDLEPythonWin 中運行。

 
 
 
 
  1. >>> printf = libc.printf
  2. >>> printf(b"Hello, %s\n", b"World!")
  3. Hello, World!
  4. 14
  5. >>> printf(b"Hello, %S\n", "World!")
  6. Hello, World!
  7. 14
  8. >>> printf(b"%d bottles of beer\n", 42)
  9. 42 bottles of beer
  10. 19
  11. >>> printf(b"%f bottles of beer\n", 42.5)
  12. Traceback (most recent call last):
  13. File "", line 1, in
  14. ArgumentError: argument 2: TypeError: Don't know how to convert parameter 2
  15. >>>

正如前面所提到過的,除了整數、字符串以及字節(jié)串之外,所有的 Python 類型都必須使用它們對應的 ctypes 類型包裝,才能夠被正確地轉換為所需的C語言類型。

 
 
 
 
  1. >>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
  2. An int 1234, a double 3.140000
  3. 31
  4. >>>

使用自定義的數據類型調用函數

你也可以通過自定義 ctypes 參數轉換方式來允許自定義類型作為參數。 ctypes 會尋找 _as_parameter_ 屬性并使用它作為函數參數。當然,它必須是數字、字符串或者二進制字符串:

 
 
 
 
  1. >>> class Bottles:
  2. ... def __init__(self, number):
  3. ... self._as_parameter_ = number
  4. ...
  5. >>> bottles = Bottles(42)
  6. >>> printf(b"%d bottles of beer\n", bottles)
  7. 42 bottles of beer
  8. 19
  9. >>>

如果你不想把實例的數據存儲到 _as_parameter_ 屬性。可以通過定義 property 函數計算出這個屬性。

指定必選參數的類型(函數原型)

可以通過設置 argtypes 屬性的方法指定從 DLL 中導出函數的必選參數類型。

argtypes 必須是一個 C 數據類型的序列 (這里的 printf 可能不是個好例子,因為它是變長參數,而且每個參數的類型依賴于格式化字符串,不過嘗試這個功能也很方便):

 
 
 
 
  1. >>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
  2. >>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
  3. String 'Hi', Int 10, Double 2.200000
  4. 37
  5. >>>

指定數據類型可以防止不合理的參數傳遞(就像 C 函數的原型),并且會自動嘗試將參數轉換為需要的類型:

 
 
 
 
  1. >>> printf(b"%d %d %d", 1, 2, 3)
  2. Traceback (most recent call last):
  3. File "", line 1, in
  4. ArgumentError: argument 2: TypeError: wrong type
  5. >>> printf(b"%s %d %f\n", b"X", 2, 3)
  6. X 2 3.000000
  7. 13
  8. >>>

如果你想通過自定義類型傳遞參數給函數,必須實現 from_param() 類方法,才能夠將此自定義類型用于 argtypes 序列。from_param() 類方法接受一個 Python 對象作為函數輸入,它應該進行類型檢查或者其他必要的操作以保證接收到的對象是合法的,然后返回這個對象,或者它的 _as_parameter_ 屬性,或者其他你想要傳遞給 C 函數的參數。這里也一樣,返回的結果必須是整型、字符串、二進制字符串、 ctypes 類型,或者一個具有 _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.

這是個更高級的例子,它調用了 strchr 函數,這個函數接收一個字符串指針以及一個字符作為參數,返回另一個字符串指針。

 
 
 
 
  1. >>> strchr = libc.strchr
  2. >>> strchr(b"abcdef", ord("d"))
  3. 8059983
  4. >>> strchr.restype = c_char_p # c_char_p is a pointer to a string
  5. >>> strchr(b"abcdef", ord("d"))
  6. b'def'
  7. >>> print(strchr(b"abcdef", ord("x")))
  8. None
  9. >>>

如果希望避免上述的 ord("x") 調用,可以設置 argtypes 屬性,第二個參數就會將單字符的 Python 二進制字符對象轉換為 C 字符:

 
 
 
 
  1. >>> strchr.restype = c_char_p
  2. >>> strchr.argtypes = [c_char_p, c_char]
  3. >>> strchr(b"abcdef", b"d")
  4. 'def'
  5. >>> strchr(b"abcdef", b"def")
  6. Traceback (most recent call last):
  7. File "", line 1, in
  8. ArgumentError: argument 2: TypeError: one character string expected
  9. >>> print(strchr(b"abcdef", b"x"))
  10. None
  11. >>> strchr(b"abcdef", b"d")
  12. 'def'
  13. >>>

如果外部函數返回了一個整數,你也可以使用要給可調用的 Python 對象(比如函數或者類)作為 restype 屬性的值。將會以 C 函數返回的 整數 對象作為參數調用這個可調用對象,執(zhí)行后的結果作為最終函數返回值。這在錯誤返回值校驗和自動拋出異常等方面比較有用。

 
 
 
 
  1. >>> GetModuleHandle = windll.kernel32.GetModuleHandleA
  2. >>> def ValidHandle(value):
  3. ... if value == 0:
  4. ... raise WinError()
  5. ... return value
  6. ...
  7. >>>
  8. >>> GetModuleHandle.restype = ValidHandle
  9. >>> GetModuleHandle(None)
  10. 486539264
  11. >>> GetModuleHandle("something silly")
  12. Traceback (most recent call last):
  13. File "", line 1, in
  14. File "", line 3, in ValidHandle
  15. OSError: [Errno 126] The specified module could not be found.
  16. >>>

WinError 函數可以調用 Windows 的 FormatMessage() API 獲取錯誤碼的字符串說明,然后 返回 一個異常。 WinError 接收一個可選的錯誤碼作為參數,如果沒有的話,它將調用 GetLastError() 獲取錯誤碼。

請注意,使用 errcheck 屬性可以實現更強大的錯誤檢查手段;詳情請見參考手冊。

傳遞指針(或以引用方式傳遞形參)

有時候 C 函數接口可能由于要往某個地址寫入值,或者數據太大不適合作為值傳遞,從而希望接收一個 指針 作為數據參數類型。這和 傳遞參數引用 類似。

ctypes 暴露了 byref() 函數用于通過引用傳遞參數,使用 pointer() 函數也能達到同樣的效果,只不過 pointer() 需要更多步驟,因為它要先構造一個真實指針對象。所以在 Python 代碼本身不需要使用這個指針對象的情況下,使用 byref() 效率更高。

 
 
 
 
  1. >>> i = c_int()
  2. >>> f = c_float()
  3. >>> s = create_string_buffer(b'\000' * 32)
  4. >>> print(i.value, f.value, repr(s.value))
  5. 0 0.0 b''
  6. >>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",
  7. ... byref(i), byref(f), s)
  8. 3
  9. >>> print(i.value, f.value, repr(s.value))
  10. 1 3.1400001049 b'Hello'
  11. >>>

結構體和聯合

結構體和聯合必須繼承自 ctypes 模塊中的 Structure 和 Union 。子類必須定義 _fields_ 屬性。 _fields_ 是一個二元組列表,二元組中包含 field namefield type 。

type 字段必須是一個 ctypes 類型,比如 c_int,或者其他 ctypes 類型: 結構體、聯合、數組、指針。

這是一個簡單的 POINT 結構體,它包含名稱為 xy 的兩個變量,還展示了如何通過構造函數初始化結構體。

 
 
 
 
  1. >>> from ctypes import *
  2. >>> class POINT(Structure):
  3. ... _fields_ = [("x", c_int),
  4. ... ("y", c_int)]
  5. ...
  6. >>> point = POINT(10, 20)
  7. >>> print(point.x, point.y)
  8. 10 20
  9. >>> point = POINT(y=5)
  10. >>> print(point.x, point.y)
  11. 0 5
  12. >>> POINT(1, 2, 3)
  13. Traceback (most recent call last):
  14. File "", line 1, in
  15. TypeError: too many initializers
  16. >>>

當然,你可以構造更復雜的結構體。一個結構體可以通過設置 type 字段包含其他結構體或者自身。

這是以一個 RECT 結構體,他包含了兩個 POINT ,分別叫 upperleftlowerright:

 
 
 
 
  1. >>> class RECT(Structure):
  2. ... _fields_ = [("upperleft", POINT),
  3. ... ("lowerright", POINT)]
  4. ...
  5. >>> rc = RECT(point)
  6. >>> print(rc.upperleft.x, rc.upperleft.y)
  7. 0 5
  8. >>> print(rc.lowerright.x, rc.lowerright.y)
  9. 0 0
  10. >>>

嵌套結構體可以通過幾種方式構造初始化:

 
 
 
 
  1. >>> r = RECT(POINT(1, 2), POINT(3, 4))
  2. >>> r = RECT((1, 2), (3, 4))

可以通過 獲取字段 descriptor ,它能提供很多有用的調試信息。

 
 
 
 
  1. >>> print(POINT.x)
  2. >>> print(POINT.y)
  3. >>>

警告

ctypes 不支持帶位域的結構體、聯合以值的方式傳給函數。這可能在 32 位 x86 平臺上可以正常工作,但是對于一般情況,這種行為是未定義的。帶位域的結構體、聯合應該總是通過指針傳遞給函數。

結構體/聯合字段對齊及字節(jié)順序

默認情況下,結構體和聯合的字段與 C 的字節(jié)對齊是一樣的。也可以在定義子類的時候指定類的 _pack_ 屬性來覆蓋這種行為。 它必須設置為一個正整數,表示字段的最大對齊字節(jié)。這和 MSVC 中的 #pragma pack(n) 功能一樣。

ctypes 中的結構體和聯合使用的是本地字節(jié)序。要使用非本地字節(jié)序,可以使用 BigEndianStructure, LittleEndianStructure, BigEndianUnion, and LittleEndianUnion 作為基類。這些類不能包含指針字段。

結構體和聯合中的位域

結構體和聯合中是可以包含位域字段的。位域只能用于整型字段,位長度通過 _fields_ 中的第三個參數指定:

 
 
 
 
  1. >>> class Int(Structure):
  2. ... _fields_ = [("first_16", c_int, 16),
  3. ... ("second_16", c_int, 16)]
  4. ...
  5. >>> print(Int.first_16)
  6. >>> print(Int.second_16)
  7. >>>

數組

數組是一個序列,包含指定個數元素,且必須類型相同。

創(chuàng)建數組類型的推薦方式是使用一個類型乘以一個正數:

 
 
 
 
  1. TenPointsArrayType = POINT * 10

下面是一個構造的數據案例,結構體中包含了4個 POINT 和一些其他東西。

 
 
 
 
  1. >>> from ctypes import *
  2. >>> class POINT(Structure):
  3. ... _fields_ = ("x", c_int), ("y", c_int)
  4. ...
  5. >>> class MyStruct(Structure):
  6. ... _fields_ = [("a", c_int),
  7. ... ("b", c_float),
  8. ... ("point_array", POINT * 4)]
  9. >>>
  10. >>> print(len(MyStruct().point_array))
  11. 4
  12. >>>

和平常一樣,通過調用它創(chuàng)建實例:

 
 
 
 
  1. arr = TenPointsArrayType()
  2. for pt in arr:
  3. print(pt.x, pt.y)

以上代碼會打印幾行 0 0 ,因為數組內容被初始化為 0.

也能通過指定正確類型的數據來初始化:

 
 
 
 
  1. >>> from ctypes import *
  2. >>> TenIntegers = c_int * 10
  3. >>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
  4. >>> print(ii)
  5. >>> for i in ii: print(i, end=" ")
  6. ...
  7. 1 2 3 4 5 6 7 8 9 10
  8. >>>

指針

可以將 ctypes 類型數據傳入 pointer() 函數創(chuàng)建指針:

 
 
 
 
  1. >>> from ctypes import *
  2. >>> i = c_int(42)
  3. >>> pi = pointer(i)
  4. >>>

指針實例擁有 contents 屬性,它返回指針指向的真實對象,如上面的 i 對象:

 
 
 
 
  1. >>> pi.contents
  2. c_long(42)
  3. >>>

注意 ctypes 并沒有 OOR (返回原始對象), 每次訪問這個屬性時都會構造返回一個新的相同對象:

 
 
 
 
  1. >>> pi.contents is i
  2. False
  3. >>> pi.contents is pi.contents
  4. False
  5. >>>

將這個指針的 contents 屬性賦值為另一個 c_int 實例將會導致該指針指向該實例的內存地址:

 
 
 
 
  1. >>> i = c_int(99)
  2. >>> pi.contents = i
  3. >>> pi.contents
  4. c_long(99)
  5. >>>

指針對象也可以通過整數下標進行訪問:

 
 
 
 
  1. >>> pi[0]
  2. 99
  3. >>>

通過整數下標賦值可以改變指針所指向的真實內容:

 
 
 
 
  1. >>> print(i)
  2. c_long(99)
  3. >>> pi[0] = 22
  4. >>> print(i)
  5. c_long(22)
  6. >>>

使用 0 以外的索引也是合法的,但是你必須確保知道自己為什么這么做,就像 C 語言中: 你可以訪問或者修改任意內存內容。 通常只會在函數接收指針是才會使用這種特性,而且你 知道 這個指針指向的是一個數組而不是單個值。

內部細節(jié), pointer() 函數不只是創(chuàng)建了一個指針實例,它首先創(chuàng)建了一個指針 類型 。這是通過調用 POINTER() 函數實現的,它接收 ctypes 類型為參數,返回一個新的類型:

 
 
 
 
  1. >>> PI = POINTER(c_int)
  2. >>> PI
  3. >>> PI(42)
  4. Traceback (most recent call last):
  5. File "", line 1, in
  6. TypeError: expected c_long instead of int
  7. >>> PI(c_int(42))
  8. >>>

無參調用指針類型可以創(chuàng)建一個 NULL 指針。 NULL 指針的布爾值是 False

 
 
 
 
  1. >>> null_ptr = POINTER(c_int)()
  2. >>> print(bool(null_ptr))
  3. False
  4. >>>

解引用指針的時候, ctypes 會幫你檢測是否指針為 NULL (但是解引用無效的 非 NULL 指針仍會導致 Python 崩潰):

 
 
 
 
  1. >>> null_ptr[0]
  2. Traceback (most recent call last):
  3. ....
  4. ValueError: NULL pointer access
  5. >>>
  6. >>> null_ptr[0] = 1234
  7. Traceback (most recent call last):
  8. ....
  9. ValueError: NULL pointer access
  10. >>>

類型轉換

通常情況下, ctypes 具有嚴格的類型檢查。這代表著, 如果在函數 argtypes 本文名稱:創(chuàng)新互聯Python教程:ctypes —- Python 的外部函數庫
標題路徑:http://www.5511xx.com/article/dpccije.html