ctypes 是 Python 标准库的外部函数接口,允许直接调用 C 动态链接库中的函数。
核心价值
- 调用系统 API:Windows Kernel32.dll、Linux libc.so
- 复用 C 库:高性能数学计算、图像处理、硬件驱动
- 跨语言交互:Rust、Fortran 等编译为 C 兼容库
加载动态库
| 加载器 | 调用约定 | 适用平台 |
|---|
CDLL / cdll | cdecl | Linux/Unix,Windows C 库 |
WinDLL / windll | stdcall | Windows API |
OleDLL / oledll | stdcall (HRESULT) | Windows COM |
from ctypes import *
# Linux/macOS
libc = CDLL("libc.so.6") # Linux
# libc = CDLL("libc.dylib") # macOS
# Windows
# libc = windll.kernel32
# 自定义库
mylib = CDLL("./mylib.so")
函数原型定义
from ctypes import *
libc = CDLL("libc.so.6")
# 设置参数和返回类型(强烈建议)
libc.atoi.argtypes = [c_char_p]
libc.atoi.restype = c_int
result = libc.atoi(b"123") # 123
数据类型
基础类型
| ctypes 类型 | C 类型 |
|---|
c_int | int |
c_double | double |
c_char_p | char* |
c_void_p | void* |
结构体
class POINT(Structure):
_fields_ = [("x", c_int), ("y", c_int)]
point = POINT(10, 20)
print(point.x, point.y) # 10 20
# 结构体嵌套
class RECT(Structure):
_fields_ = [("ul", POINT), ("lr", POINT)]
数组
# 定义数组类型
IntArray10 = c_int * 10
arr = IntArray10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
print(arr[0]) # 1
指针
# 创建指针
i = c_int(42)
pi = pointer(i)
print(pi.contents) # c_long(42)
# 传递引用(更高效)
mylib.my_function(byref(i))
# 可写字符串缓冲区
buf = create_string_buffer(b"Hello", 10)
print(buf.value) # b'Hello'
回调函数
from ctypes import *
# 定义回调类型:返回值 + 参数类型
CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
@CMPFUNC
def py_cmp(a, b):
return a[0] - b[0]
# 用于 qsort
IntArray5 = c_int * 5
ia = IntArray5(5, 1, 7, 33, 99)
libc.qsort(ia, len(ia), sizeof(c_int), py_cmp)
print(list(ia)) # [1, 5, 7, 33, 99]
实战示例
调用自定义 C 库
// mathlib.c
double hypotenuse(double a, double b) {
return sqrt(a*a + b*b);
}
gcc -shared -fPIC -o libmathlib.so mathlib.c -lm
mathlib = CDLL('./libmathlib.so')
mathlib.hypotenuse.argtypes = [c_double, c_double]
mathlib.hypotenuse.restype = c_double
result = mathlib.hypotenuse(3.0, 4.0) # 5.0
Windows API
from ctypes import *
from ctypes.wintypes import *
kernel32 = windll.kernel32
handle = kernel32.GetCurrentProcess()
# 获取模块路径
kernel32.GetModuleFileNameA.argtypes = [c_void_p, c_char_p, c_uint]
kernel32.GetModuleFileNameA.restype = c_uint
buf = create_string_buffer(1024)
kernel32.GetModuleFileNameA(None, buf, len(buf))
print(buf.value.decode())
最佳实践
- 类型安全:始终设置
argtypes 和 restype
- 调用约定:Windows 上区分
cdll(cdecl) 和 windll(stdcall)
- 内存管理:明确 C 函数内分配内存的所有权
- 错误处理:检查返回值,使用
faulthandler 调试崩溃
- 跨平台:用
ctypes.util.find_library 查找库
替代方案对比
| 技术 | 优点 | 缺点 |
|---|
| ctypes | 标准库,无需编译 | 性能开销大 |
| CFFI | 更 Pythonic | 需单独安装 |
| Cython | 性能极佳 | 需要编译 |