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

如何用ctypes将C DLL的结构体二级指针内容读取到Python

解决Windows下Python 3.4用ctypes读取C DLL返回的结构体二级指针问题

遇到这种情况,大概率是二级指针的参数传递和类型映射没处理对。我来一步步给你拆解解决方案,先从C端的代码假设(按常见场景模拟),再对应Python端的实现:

第一步:明确C端的结构体和导出函数示例

假设你的C代码大概是这样的(如果和实际代码有差异,可对应调整):

#include <stdlib.h>
#include <string.h>

// 定义你的结构体
typedef struct {
    int id;
    char name[20];
    float score;
} Student;

// 导出函数:通过二级指针返回结构体数组,同时用第二个参数返回数组长度
__declspec(dllexport) void GetStudentData(Student** pOutData, int* pOutCount) {
    // 模拟生成2条数据
    *pOutCount = 2;
    // 分配内存
    *pOutData = (Student*)malloc(*pOutCount * sizeof(Student));
    
    // 填充第一条数据
    (*pOutData)[0].id = 1001;
    strcpy((*pOutData)[0].name, "ZhangSan");
    (*pOutData)[0].score = 92.5f;
    
    // 填充第二条数据
    (*pOutData)[1].id = 1002;
    strcpy((*pOutData)[1].name, "LiSi");
    (*pOutData)[1].score = 88.0f;
}

// 必须导出释放内存的函数!避免内存泄漏
__declspec(dllexport) void FreeStudentData(Student* pData) {
    free(pData);
}

第二步:Python 3.4端的ctypes实现

重点是正确映射结构体类型、处理二级指针参数,代码如下:

import ctypes

# 1. 定义对应C结构体的Python类,继承ctypes.Structure
class Student(ctypes.Structure):
    _fields_ = [
        ("id", ctypes.c_int),
        ("name", ctypes.c_char * 20),  # 对应C的char[20]
        ("score", ctypes.c_float)
    ]

# 2. 加载你的DLL(替换成你的DLL路径)
dll = ctypes.WinDLL("YourDllName.dll")  # 如果是cdecl调用约定,用ctypes.CDLL

# 3. 设置GetStudentData函数的参数类型和返回类型
# 函数原型:void GetStudentData(Student**, int*)
dll.GetStudentData.argtypes = [
    ctypes.POINTER(ctypes.POINTER(Student)),  # 对应C的Student**
    ctypes.POINTER(ctypes.c_int)             # 对应C的int*
]
dll.GetStudentData.restype = None  # 返回值是void

# 设置FreeStudentData函数的类型
dll.FreeStudentData.argtypes = [ctypes.POINTER(Student)]
dll.FreeStudentData.restype = None

# 4. 调用函数并获取数据
def get_student_data():
    # 准备接收输出的参数
    p_data = ctypes.POINTER(Student)()  # 初始化一个空的指针,对应C的Student*
    count = ctypes.c_int(0)             # 初始化长度变量
    
    # 调用DLL函数:注意要传指针的地址(二级指针)
    dll.GetStudentData(ctypes.byref(p_data), ctypes.byref(count))
    
    # 读取数据:把指针转换成结构体数组
    student_list = []
    for i in range(count.value):
        # 通过索引访问结构体数组元素
        student = p_data[i]
        student_list.append({
            "id": student.id,
            "name": student.name.decode("gbk"),  # Windows默认GBK编码,按需调整
            "score": student.score
        })
    
    # 必须释放内存!否则会造成泄漏
    dll.FreeStudentData(p_data)
    
    return student_list, count.value

# 测试调用
if __name__ == "__main__":
    students, total = get_student_data()
    print(f"总共获取到{total}条数据:")
    for s in students:
        print(s)

关键注意事项(可能是你之前失败的原因)

  • 二级指针的传递:C函数需要修改Student*的指向,所以Python端必须传递POINTER(POINTER(Student))类型,用ctypes.byref(p_data)来传递指针的地址,而不是直接传p_data。
  • 结构体字段的严格对应:C结构体的字段类型、顺序必须和Python的_fields_完全一致,比如char数组要用ctypes.c_char * 长度,不能出错。
  • 调用约定:如果你的C函数用的是cdecl(比如GCC编译的),必须用ctypes.CDLL加载;如果是stdcall(MSVC默认),用ctypes.WinDLL。调用约定不匹配会导致参数栈混乱,数据读取错误。
  • 内存释放:C端malloc的内存,Python端无法直接释放,必须调用DLL提供的释放函数,否则会造成内存泄漏。

排查你之前的问题

如果还是拿不到pReturn的数据,你可以检查这几点:

  • 有没有正确设置函数的argtypes?如果不设置,ctypes会默认转换参数类型,可能导致指针传递错误。
  • 结构体的字段定义是否和C端完全一致?比如字段顺序、类型长度(比如char数组的长度)。
  • 是不是忘记用ctypes.byref传递指针的地址?直接传指针变量的话,C函数无法修改它的指向。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:07:36