调试程序,对动态申请的内存用free或者delete释放时程序崩溃,跳出如下对话框:
点击重试,定位到具体的CRT源码部分:_ASSERTE(_CrtIsValidHeapPointer(pUserData));
1、原因分析:
查看CRT源码,一步一步看看里面都干了什么吧: _CrtIsValidHeapPointer----->_CrtIsValidPointer---->HeapValidate
首先在_CrtIsValidPointer中检测这个指针是否有效,即不为空;然后调用WIndows API HeapValidate,这个才是关键了,CRT 代码中C++的乐网new 调用的是C语言的malloc,delete调用的是C语言的free。由于C库函数是跨平台的,那么CRT的意义就在于在Windows平台上封装WIN API 来实现这些功能。
HeapValidate功能是检测一个内存指针是否是在这个堆栈上面申请的。每个PE文件在加载时都会初始化CRT运行库,调用heapinit.c 中的_heap_init,_heap_init中还是用的WIN API HeapCreate初始化堆栈,然后把这个堆栈的句柄保存到一个全局变量中,后续这个进程在堆栈上申请释放内存都要用到这个句柄。上面的崩溃原因就是因为释放内存的指针不是在该PE文件的堆栈上申请的。
具体说,我的问题其实就是在DLL中申请了一块内存,在EXE中删除这块内存。但是这两个PE(exe、dll都是PE文件)在初始化时分配的堆栈句柄是不一样的,所以就出错了。
2、解决方法:
问题原因清楚了,其实就是要我们注意,在谁的堆栈上分配的内存,就在谁的堆栈上释放。
方案一:在DLL里面申请的内存,就在DLL里面释放,不要由其他模块去释放;(为了区分第二条,这个前提是exe 和 dll都使用mtd方式编译)
方案二:让所有的PE共享一个堆栈句柄,所有的模块都采用的mdd方式编译;
(上面说的是Debug选项下),原因在于,采用mdd编译时,所有的模块都会共享同一个msvcr*.dll,堆栈句柄作为静态变量保存在里面,所以所有的模块都是同一个堆栈上申请、释放内存,不管哪个模块都可以释放其他模块申请的内存了。而采用mtd编译时,所有模块都相当于有自己独立的一个mscvr*.dll(相当于把msvcr*.dll中需要的代码都打包到自己里面去了),因此每个模块在加载的时候都有自己独立的堆栈,这时去删除其他模块申请的内存,堆栈检测该指针肯定就是无效的了。
3、后记
码字好累,语文学的不好,说的有点绕。希望对遇到该问题的人有所帮助,谢谢。