如何用Python的ctypes访问C动态库中的typedef结构体与全局结构体?
问题:用ctypes访问C共享库全局结构体的错误与解决
背景
我有一批C编写的函数需要在Python中运行,使用ctypes访问typedef结构体时遇到异常。核心需求是:访问共享库中存储的全局C结构体,或复用C头文件的typedef定义在Python中重建匹配的结构体;同时怀疑编译SO文件的方式是否未正确导出全局变量。
相关代码与编译命令
temp.c 文件
#include "temp.h" aStruct_t aStruct = {0}; extern void AdderFunction(aStruct_t *struct1, int num) { struct1->y = struct1->x; struct1->x += num; }
temp.h 文件
#ifndef _TEMP_H_ #define _TEMP_H_ #include <stdint.h> typedef struct aStruct_t { uint32_t x; uint32_t y; uint32_t z; } aStruct_t; extern void AdderFunction(aStruct_t *struct1, int num); #endif
编译命令
gcc -shared -o temp.so -fPIC temp.c
尝试的Python代码与错误
我尝试直接访问全局结构体aStruct,代码如下:
import ctypes so_file = "../temp.so" tempLib = ctypes.CDLL(so_file) tempLib.aStruct.y
执行时触发错误:AttributeError: '_FuncPtr' object has no attribute 'y',困惑点在于:为什么typedef结构体被识别成了函数指针类型?
目前临时解决方法是在Python中手动定义匹配的结构体,但这种方式效率极低——C代码中有多个大型结构体,每次C结构体更新都需要同步修改Python代码:
import ctypes so_file = "../temp.so" tempLib = ctypes.CDLL(so_file) class aStruct_python(ctypes.Structure): _fields_ = [("x", ctypes.c_uint32), ("y", ctypes.c_uint32), ("z", ctypes.c_uint32)] struct_py = aStruct_python() tempLib.AdderFunction(ctypes.pointer(struct_py), 2)
错误原因
- 符号类型识别偏差:ctypes默认会将共享库中的符号当作函数指针处理,无法自动区分符号是变量还是函数,因此直接访问
tempLib.aStruct会被解析为函数指针而非全局结构体变量。 - 结构体类型未绑定:即使获取到全局变量的地址,ctypes也不知道其对应的结构体类型,必须先在Python中定义匹配的结构体类型,才能正确解析内存结构。
解决方案
方法1:正确访问共享库中的全局结构体
先在Python中定义与C结构体一致的类型,再显式告诉ctypes该符号是变量而非函数:
import ctypes # 1. 定义与C中匹配的结构体类型 class aStruct_t(ctypes.Structure): _fields_ = [ ("x", ctypes.c_uint32), ("y", ctypes.c_uint32), ("z", ctypes.c_uint32) ] so_file = "../temp.so" tempLib = ctypes.CDLL(so_file) # 2. 从共享库中获取全局变量,指定其类型为结构体指针 aStruct_global = ctypes.POINTER(aStruct_t).in_dll(tempLib, "aStruct") # 3. 访问结构体成员或调用函数修改 print(f"初始x值: {aStruct_global.contents.x}") tempLib.AdderFunction(aStruct_global, 2) print(f"修改后x值: {aStruct_global.contents.x}") print(f"修改后y值: {aStruct_global.contents.y}")
方法2:自动生成Python结构体绑定(避免手动同步)
如果存在大量需同步的结构体,可使用工具自动生成ctypes绑定代码:
- 使用
ctypesgen工具,直接解析C头文件生成对应Python代码:
之后在Python中导入生成的模块,即可直接使用ctypesgen temp.h -o temp_bindings.pyaStruct_t等类型,无需手动定义。
编译验证
你的编译命令是正确的,全局变量aStruct会被导出到SO中。可通过以下命令验证符号是否存在:
nm temp.so | grep aStruct
若输出类似0000000000004030 D aStruct,说明变量已正确导出(D表示全局数据符号)。
内容的提问来源于stack exchange,提问作者Rob Robinson
相关产品推荐
相关产品推荐

