You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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)

错误原因

  1. 符号类型识别偏差:ctypes默认会将共享库中的符号当作函数指针处理,无法自动区分符号是变量还是函数,因此直接访问tempLib.aStruct会被解析为函数指针而非全局结构体变量。
  2. 结构体类型未绑定:即使获取到全局变量的地址,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代码:
    ctypesgen temp.h -o temp_bindings.py
    
    之后在Python中导入生成的模块,即可直接使用aStruct_t等类型,无需手动定义。

编译验证

你的编译命令是正确的,全局变量aStruct会被导出到SO中。可通过以下命令验证符号是否存在:

nm temp.so | grep aStruct

若输出类似0000000000004030 D aStruct,说明变量已正确导出(D表示全局数据符号)。

内容的提问来源于stack exchange,提问作者Rob Robinson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 06:18:20