Python调用C++ DLL时结构体内存重分配引发堆错误的解决咨询
问题描述
我用C++编写了一个DLL,想要在Python中调用。该DLL包含一个带多个字段的结构体,其中一个字段为float*类型。当DLL内的方法修改这个“数组”的大小时,Python会抛出堆错误(heap error)。
目前我已通过ctypes在Python中实现了对应结构体,调用仅修改固定长度字段(如int、float、固定长度char数组等)的DLL方法时一切正常,但只要调用修改float*字段大小的方法就会触发堆错误。
请问有没有解决办法?还是必须保证float*数组在调用DLL方法前就拥有正确长度?
可复现代码示例
C++ 文件
MyCode.h
#pragma once #include <sal.h> typedef struct MyStruct { // 数组'A'的数据点数量 unsigned int length; // 数值字段 float value; // 数据数组 float* A; }MyStruct; extern "C" { void __cdecl ChangeValue(_Inout_ MyStruct* data, float newValue); void __cdecl ResizeDataArray(_Inout_ MyStruct* data, unsigned int newLength); }
MyCode.cpp
#include <sal.h> #include "MyCode.h" extern "C" void __cdecl ChangeValue(_Inout_ MyStruct * data, float newValue) { data->value = newValue; } extern "C" void __cdecl ResizeDataArray(_Inout_ MyStruct * data, unsigned int newLength) { if (data == nullptr) { return; } // 若目标长度不同则调整数组大小 if (data->length != newLength) { if (data->length > 0 && data->A != nullptr) { delete[] data->A; data->length = 0U; } // 分配内存并初始化为0 data->A = new float[newLength]; memset(data->A, 0, newLength * sizeof(float)); data->length = newLength; } }
StructTool.def
LIBRARY "StructTool" EXPORTS ChangeValue ResizeDataArray
Python 代码
import os import ctypes as c class MyStruct(c.Structure): _fields_ = [ ("length", c.c_uint), ("value", c.c_float), ("A", c.POINTER(c.c_float)) ] if __name__ == '__main__': dllPath = r'C:\MyFolder' dllFullPath = os.path.join(dllPath, 'StructTool.dll') if not os.path.exists(dllFullPath): print('DLL '+ dllFullPath +' not found') exit(1) os.chdir(dllPath) my_dll = c.CDLL(dllFullPath) # 加载DLL # 创建结构体实例 testStruct = MyStruct() # 分配float数组并初始化结构体 array_size = 10 testStruct.A = (c.c_float * array_size)() testStruct.length = array_size testStruct.value = 0.12345 print("testStruct.length: ", testStruct.length) print("testStruct.value: ", testStruct.value, "\n") # 修改value字段 - 运行正常 my_dll.ChangeSingleValue.argtypes = [c.POINTER(MyStruct), c.c_float] newValue = 123.456 my_dll.ChangeSingleValue(c.byref(testStruct), newValue) print("testStruct.value: ", testStruct.value, "\n") # 修改A字段长度 - 触发堆错误! my_dll.ResizeDataArray.argtypes = [c.POINTER(MyStruct), c.c_uint] newLength = 15 my_dll.ResizeDataArray(c.byref(testStruct), newLength) # 此处抛出堆错误 print("testStruct.length: ", testStruct.length)
解决方案
堆错误的核心原因是跨堆内存操作:
- Python通过
(c.c_float * array_size)()分配的内存属于Python进程的堆(由Python关联的C运行时库管理); - DLL中用
delete[]释放这块内存时,是用DLL自身的C运行时堆管理器去操作不属于它的内存,直接导致堆损坏。
方法一:让DLL全权负责内存生命周期(推荐)
修改Python代码,初始化时不手动分配数组,而是通过DLL的函数完成内存分配;同时新增DLL函数负责内存释放,避免跨堆操作。
调整后的Python初始化代码
# 初始化结构体时,不要手动分配A testStruct = MyStruct() testStruct.length = 0 testStruct.value = 0.12345 testStruct.A = None # 初始化为空指针 # 调用DLL函数分配初始大小的数组 my_dll.ResizeDataArray.argtypes = [c.POINTER(MyStruct), c.c_uint] initial_size = 10 my_dll.ResizeDataArray(c.byref(testStruct), initial_size)
DLL新增内存释放函数
在MyCode.h中添加声明:
extern "C" { // ... 原有函数声明 void __cdecl FreeDataArray(_Inout_ MyStruct* data); }
在MyCode.cpp中实现:
extern "C" void __cdecl FreeDataArray(_Inout_ MyStruct * data) { if (data != nullptr && data->A != nullptr) { delete[] data->A; data->A = nullptr; data->length = 0U; } }
Python中调用释放函数
my_dll.FreeDataArray.argtypes = [c.POINTER(MyStruct)] my_dll.FreeDataArray(c.byref(testStruct))
方法二:统一内存分配器(兼容性差)
确保Python和DLL使用完全相同版本的动态链接C运行时库(如均用/MD编译选项),让两者共享同一个堆管理器。但该方法依赖运行环境的一致性,一旦运行时版本匹配失败就会再次出现错误,不推荐用于生产环境。
内容的提问来源于stack exchange,提问作者Ace76
相关产品推荐
相关产品推荐

