python ctypes
Table of Contents
1 Python中調用C語言庫
Python中提供了ctypes標準庫,用來調用C語言庫中的函數。
- 加載dll C語言函數存在不同的調用約定,常見的有stdcall和cdecl,這兩種調用約定在Python中的調用函數需要區分:
- stdcall的加載方式: dll = windll.LoadLibrary("dllpath") dll = WinDLL("dllpath")
- cdecl的加載方式: dll = cdll.LoadLibrary("dllpath") dll = CDLL("dllpath")
- 調用函數,傳入參,返回值 C函數通過參數讀取值,做處理後通過返回值傳回。
#include <stdlib.h> // file add.c #include <stdio.h> int add(const int a, const int b) { return a+b; }
把C函數編譯爲.so庫,執行一下命令: gcc -shared -fPIC -o libadd.so add.c
from ctypes import * dll = CDLL("./libadd.so") print("add(1,2) = ", dll.add(1, 2))
執行Python命令,查看執行情況: python3 ./add.py add(1,2) = 3
- C函數通過參數傳出值,即參數是指針的情況 C語言指針概念在ctypes中用pointer對應,如下示例:
#include <stdlib.h> // file add.c #include <stdio.h> int add(const int a, const int b) { return a+b; } int add2(const int a, const int b, int *c) { if (NULL != c) *c = a+b; return a+b; }
add2函數傳入指針,並可通過指針輸出值。 python調用函數如下:
from ctypes import * dll = CDLL("./libadd.so") print("add(1,2) = ", dll.add(1, 2)) out = c_int(0) ptr = pointer(out) out.value = 100 print("out.value = ", out.value) dll.add2(2,3,ptr) print("add2(2, 3) = ", out.value)
python3 ./add.py add(1,2) = 3 out.value = 100 add2(2, 3) = 5
- 參數是結構體指針 結構體通過繼承Structure類實現,各字段通過field定義,示例如下:
typedef struct { int x; int y; } Point; int add3(const Point a, const Point b, Point *c) { if (NULL == c) return 255; c->x = a.x + b.x; c->y = a.y + b.y; return 0; }
Python中的定義如下:
class Point(Structure): _fields_ = ("x", c_int), ("y", c_int) def __repr__(self): return "(%d, %d)" % (self.x, self.y) p1 = Point(1,2) p2 = Point(5, 8) p3 = Point(0,0) ptr_p3 = pointer(p3) dll.add3(p1,p2,ptr_p3) print("add3(p1,p2) = ", p3)
其中_repr_是python的內置函數,這個是類的展示函數,相當於java語言中的toString()函數。
- 入參char*指針
int pr(const char *name, char *out) { printf("Hello, %s\n", name); sprintf(out, "Hello, %s\n", name); return 0; }
- 方法1
sbuf = create_string_buffer(b'robin') out = create_string_buffer(100) dll.pr(sbuf, out) print("1--", out.value)
- 方法2
sbuf2 = b'\0'*10 pStr = c_char_p() pStr.value = sbuf2 dll.pr(b'John', pStr) print("2--", sbuf2)
- 方法3
sbuf3 = b'\0'*20 dll.pr(b'Rich', sbuf3) print("3--", sbuf3)
- 方法1
- 字節序處理 fields定義的每個參數的類型後面可以定義bit位,示例如下:
int swap32(const int x) { return htonl(x); }
Python中測試字節序如下:
class Int(Structure): _fields_ = [("uint8_0", c_int, 8), ("uint8_1", c_int, 8), ("uint8_2", c_int, 8), ("uint8_3", c_int, 8)] def __init__(self, i): i0 = i & 0xFF i1 = (i >> 8) & 0xFF i2 = (i >> 16) & 0xFF i3 = (i >> 24) & 0xFF super().__init__(i0,i1,i2,i3) def __repr__(self): return "0x%08X" % ((self.uint8_3<<24) + \ (self.uint8_2<<16) + \ (self.uint8_1<<8) + \ (self.uint8_0)) i = Int(0x12345678) i_big = dll.swap32(i) print( i, "0x%08X" % i_big)
這裏使用Int主要是爲了測試bit的定義,這個用例用Python內置的int類型ibig=dll.swap32(0x12345678)就可以。