

Python,速成心法
敲代码,查资料,问度娘
练习,探索,总结,优化

★★★★★博文创作不易,源码使用过程中,如有疑问的地方,欢迎大家指正留言交流。喜欢的老铁可以多多点赞+收藏分享+置顶,小红牛在此表示感谢。★★★★★
ctypes 是 Python 标准库中的一个外部函数接口(FFI)模块,它允许 Python 代码直接调用 C 语言编写的动态链接库(如 Windows 上的 .dll、Linux 上的 .so、macOS 上的 .dylib),并实现 Python 与 C 之间的数据类型转换。
核心功能
以下是一个使用 ctypes 模块的综合示例,演示了加载动态库、定义结构体、调用函数、传递指针/数组、使用回调函数等常见操作。本示例基于标准 C 库(Linux/macOS 下的 libc,Windows 下需稍作调整),可直接运行。

↓ 源码如下 ↓
# -*- coding: utf-8 -*-# @Author : 小红牛# 微信公众号:wdPythonimport ctypesimport sys# 1. 加载 C 标准库(跨平台尝试)if sys.platform == "win32":libc = ctypes.CDLL("msvcrt.dll") # Windows 下的 C 运行库else:libc = ctypes.CDLL("libc.so.6") # Linux 常用# 若失败可尝试 libc.dylib (macOS) 或使用 ctypes.util.find_library# 2. 定义 C 结构体(对应 struct tm)class StructTm(ctypes.Structure):_fields_ = [("tm_sec", ctypes.c_int),("tm_min", ctypes.c_int),("tm_hour", ctypes.c_int),("tm_mday", ctypes.c_int),("tm_mon", ctypes.c_int),("tm_year", ctypes.c_int),("tm_wday", ctypes.c_int),("tm_yday", ctypes.c_int),("tm_isdst", ctypes.c_int),]# 3. 设置函数参数类型和返回类型(类型安全检查)# time_t time(time_t *t)libc.time.argtypes = [ctypes.POINTER(ctypes.c_long)]libc.time.restype = ctypes.c_long# struct tm *localtime(const time_t *timep)libc.localtime.argtypes = [ctypes.POINTER(ctypes.c_long)]libc.localtime.restype = ctypes.POINTER(StructTm)# size_t strftime(char *s, size_t max, const char *format, const struct tm *tm)libc.strftime.argtypes = [ctypes.c_char_p, ctypes.c_size_t, ctypes.c_char_p, ctypes.POINTER(StructTm)]libc.strftime.restype = ctypes.c_size_t# int printf(const char *format, ...) 可变参数需要特殊处理,这里只演示固定签名libc.printf.argtypes = [ctypes.c_char_p]libc.printf.restype = ctypes.c_int# 4. 调用函数示例:获取当前时间并格式化输出def demo_time():# 调用 time(NULL) 获得时间戳t = libc.time(None)print(f"Raw time_t: {t}")# 调用 localtime 转换为结构体指针tm_ptr = libc.localtime(ctypes.byref(ctypes.c_long(t)))# 解引用指针得到结构体内容tm_struct = tm_ptr.contentsprint(f"Current tm: {tm_struct.tm_hour}:{tm_struct.tm_min}:{tm_struct.tm_sec}")# 使用 strftime 格式化时间字符串buf = ctypes.create_string_buffer(100) # 可写缓冲区fmt = b"%Y-%m-%d %H:%M:%S"n = libc.strftime(buf, len(buf), fmt, tm_ptr)print(f"Formatted time: {buf.value.decode()} (length={n})")# 5. 使用数组:调用 qsort 对整数排序(回调函数演示)# 定义比较回调类型:int cmp(const void *a, const void *b)CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p)@CMPFUNCdef py_cmp(a, b):# 将 void* 转换为 int* 并解引用x = ctypes.cast(a, ctypes.POINTER(ctypes.c_int)).contents.valuey = ctypes.cast(b, ctypes.POINTER(ctypes.c_int)).contents.valuereturn x - ydef demo_qsort():# 准备一个整数数组IntArray5 = ctypes.c_int * 5arr = IntArray5(42, 7, 13, 99, 5)print("Before sort:", list(arr))# 获取 qsort 函数(某些平台需显式声明)libc.qsort.argtypes = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_size_t, CMPFUNC]libc.qsort.restype = None# 调用 qsortlibc.qsort(arr, len(arr), ctypes.sizeof(ctypes.c_int), py_cmp)print("After sort: ", list(arr))# 6. 指针和 byref 用法:修改变量值def demo_pointer():x = ctypes.c_int(100)print(f"Before: {x.value}")# 假设有一个 C 函数 void add_one(int *p) { *p += 1; }# 我们手动模拟:通过 ctypes 直接操作指针# 注意:这里没有真实的 add_one,仅演示指针操作ptr = ctypes.pointer(x) # 获取 POINTER(c_int)ptr.contents.value += 1 # 等价于 *ptr += 1print(f"After pointer increment: {x.value}")# 使用 byref 传递引用(更高效,通常用于函数参数)# 例如:libc.time(ctypes.byref(t_var))# 7. 错误处理:检查函数返回值def demo_error_handling():# 故意调用一个可能失败的函数:打开不存在的文件 (fopen)if sys.platform != "win32":libc.fopen.argtypes = [ctypes.c_char_p, ctypes.c_char_p]libc.fopen.restype = ctypes.c_void_pfp = libc.fopen(b"/nonexistent/file.txt", b"r")if fp == 0: # NULL 指针print("Error: fopen failed (errno handled elsewhere)")else:# 正常关闭文件(演示用)libc.fclose.argtypes = [ctypes.c_void_p]libc.fclose(fp)else:print("Skipping fopen demo on Windows (uses different API)")# 运行所有示例if __name__ == "__main__":print("=== CTime Demo ===")demo_time()print("\n=== QSort with Callback ===")demo_qsort()print("\n=== Pointer Manipulation ===")demo_pointer()print("\n=== Error Handling ===")demo_error_handling()
完毕!!感谢您的收看
--------★★历史博文集合★★--------
