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

Python ctypes封装C++ API如何传NULL结构体指针并获取更新值

Python ctypes封装C++ API 传NULL指针及同步结构体值方案

核心问题

  • 调用op_init接口时需传入NULL结构体指针,需明确Python侧传参实现
  • 接口传入非空结构体指针时,会将从配置文件读取到的值写入指针指向内存,需明确如何同步动态库更新的结构体数值到Python对象

前置:C侧接口与实现参考

头文件定义 cpp_header.hpp

typedef enum {
    E_FLAG_ON = 0,
    E_FLAG_OFF
} option;

typedef struct {
    float *a;
    float b;
    char *file_path; 
    option flag;
} inputs;

// API
int op_init(const inputs*);

接口实现逻辑 Implementation.cpp

int op_init(const inputs* usr_ptr) {
    inputs local_struct;
    internal_opration_read_set(&local_struct) { // 读取输入文件更新local_struct的值
    }
    if (usr_ptr !=  NULL) {
        internal_opration_update_set(usr_ptr, &local_struct) {
            // 仅当传入非空指针时,将读取到的配置写入用户传入的结构体
        }
    }
}

C侧NULL传参调用示例 test.cpp

int main() {
    inputs *usr_cfg = NULL;
    op_init(usr_cfg);
}

现有ctypes初步封装代码 ctypes_imple.py

from ctypes import *

class inputs(Structure):
    _fields_ = [('a', POINTER(c_float)),
                ('b', c_float),
                ('file_path', c_char_p),
                ('flag', option)]

# loading so file
so_lib = CDLL('some so')
# how we can initialize NULL structure pointer here?
so_lib.op_init() # how to send structure pointer as argument?

完整实现方案

1. 补全类型定义,显式声明函数签名

首先补全枚举类型定义,加载动态库后必须显式设置函数的参数类型和返回值类型,否则ctypes默认按int传参,64位环境下会出现指针截断、传参崩溃问题:

from ctypes import *

# 对应C侧option枚举,C枚举默认底层类型为int
class option(c_int):
    E_FLAG_ON = 0
    E_FLAG_OFF = 1

class inputs(Structure):
    # 字段顺序、类型必须和C侧定义完全一致,否则内存偏移错误会导致值错乱
    _fields_ = [
        ('a', POINTER(c_float)),
        ('b', c_float),
        ('file_path', c_char_p),
        ('flag', option)
    ]

# 加载动态库,替换为实际so文件路径
so_lib = CDLL('./lib_your_api.so')
# 声明op_init的返回值类型为int
so_lib.op_init.restype = c_int
# 声明op_init的参数类型为指向inputs结构体的指针
so_lib.op_init.argtypes = [POINTER(inputs)]

2. 传入NULL结构体指针的实现

ctypes中None直接对应C侧的NULL指针,传None即可实现和C侧传NULL完全一致的效果:

# 等价于C侧 inputs *usr_cfg = NULL; op_init(usr_cfg);
ret_code = so_lib.op_init(None)

3. 同步动态库更新的结构体值

ctypes定义的结构体实例内存布局和C侧完全兼容,只需在Python侧实例化inputs对象,将对象指针传入接口,C侧写入内存后,Python侧直接访问实例属性即可拿到更新后的值,无需额外同步操作:

# 实例化结构体对象
usr_cfg = inputs()
# 传入结构体指针,C侧会将读取到的配置写入该对象对应的内存
ret_code = so_lib.op_init(byref(usr_cfg))

# 调用完成后直接读取属性即可获取更新后的值
print(f"字段b的值: {usr_cfg.b}")
print(f"flag字段的值: {usr_cfg.flag.value}") # 枚举类型取.value拿到实际数值

# 指针类型字段访问注意判空,c_char_p为bytes类型需解码为字符串
if usr_cfg.a:
    print(f"指针a指向的浮点值: {usr_cfg.a[0]}")
if usr_cfg.file_path:
    print(f"配置文件路径: {usr_cfg.file_path.decode('utf-8')}")

注意事项

  • 禁止省略argtypes和restype的声明,这是ctypes封装最常见的出错点
  • 如果C侧结构体用了#pragma pack等特殊内存对齐设置,需要在Python结构体类中设置_pack_属性和C侧对齐值一致
  • 结构体中a、file_path是C侧分配内存的指针,除非API文档明确要求调用指定释放接口,否则不要在Python侧手动释放,避免内存错误
  • 推荐用byref()传递结构体指针,比pointer()性能更高

内容的提问来源于stack exchange,提问作者soundarrajan v

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 21:57:21