一般的内存泄漏检查的确是很困难,但是也不是完全没有办法.如果你用VC的库来写东西的话,那么很幸运的是,你已经有了很多检查内存泄漏的工具,只是你想不想用的问题了. Visual C++的Debug版本的C运行库(C Runtime Library).它已经提供好些函数来帮助你诊断你的代码和跟踪内存泄漏. 而且最方便的地方是这些函数在Release版本中完全不起任何作用,这样就不会影响你的Release版本程序的运行效率.
比如下面的例子里面,有一个明细的内存泄漏.当然如果只有这么几行代码的话,是很容易看出有内存泄漏的.但是想在成千上万行代码里面检查内存泄漏问题就不是那么容易了.
char * pstr = new char[5];
lstrcpy(pstr,"Memory leak");
我们如果我们在Debug版本的Code里面对堆(Heap)进行了操作,包括malloc, free, calloc, realloc, new, 和 delete可以利用VC Debug运行时库中堆Debug函数来做堆的完整性和安全性检查. 比如上面的代码,lstrcpy的操作明显破坏了pstr的堆结构.使其溢出,并破坏了临近的数据.那我们可以在调用lstrcpy之后的代码里面加入_CrtCheckMemory函数._CrtCheckMemory函数发现前面的lstrcpy使得pstr的堆结构被破坏,会输出这样的报告:
emory check error at 0x00372FA5 = 0x79, should be 0xFD.
memory check error at 0x00372FA6 = 0x20, should be 0xFD.
memory check error at 0x00372FA7 = 0x6C, should be 0xFD.
memory check error at 0x00372FA8 = 0x65, should be 0xFD.
DAMAGE: after Normal block (#41) at 0x00372FA0.
Normal located at 0x00372FA0 is 5 bytes long.
它告诉说 pstr的长度应该时5个Bytes,但是在5Bytes后面的几个Bytes也被非法改写了.提醒你产生了越界操作.
_CrtCheckMemory的返回值只有TRUE和FALSE,那么你可以用_ASSERTE()来报告出错信息. 上面的语句可以换成 _ASSERTE(_CrtCheckMemory()); 这样Debug版本的程序在运行的时候就会弹出一个警告对话框,如图1,这样就不用在运行时候一直盯着Output窗口看了.这个时候按Retry,就可以进入源代码调试了.看看问题到底出在哪里
图1
其他类似的函数还有_CrtDbgReport, _CrtDoForAllClientObjects, _CrtDumpMemoryLeaks,_CrtIsValidHeapPointer, _CrtIsMemoryBlock, _CrtIsValidPointer,_CrtMemCheckpoint, _CrtMemDifference, _CrtMemDumpAllObjectsSince, _CrtMemDumpStatistics, _CrtSetAllocHook, _CrtSetBreakAlloc, _CrtSetDbgFlag,_CrtSetDumpClient, _CrtSetReportFile, _CrtSetReportHook, _CrtSetReportMode
这些函数全部都可以用来在Debug版本中检查内存的使用情况.具体怎么使用这些函数就不在这里说明了,各位可以去查查MSDN.
在这些函数中用处比较大的,或者说使用率会比较高的函数是,_CrtMemCheckpoint 设置一个内存检查点.这个函数会取得当前内存的运行状态. _CrtMemDifference 检查两种内存状态的异同. _CrtMemDumpAllObjectsSince 从程序运行开始,或者从某个内存检查点开始Dump出堆中对象的信息. 还有就是_CrtDumpMemoryLeaks当发生内存溢出的时候Dump出堆中的内存信息. _CrtDumpMemoryLeaks一般都在有怀疑是内存泄漏的代码后面调用,比如下面的例子
#include <windows.h>
#include <crtdbg.h>
void main()
{
char * pstr;
pstr = new char[5];
_CrtDumpMemoryLeaks();
}
输出:
Detected memory leaks! à提醒你,代码有内存泄漏.
Dumping objects ->
{44} normal block at 0x00372DB8, 5 bytes long.
Data: < > CD CD CD CD CD
Object dump complete.
如果你双击包含行文件名的输出行,指针将会跳到源文件中内存被分配地方的行.
当无法确定那些代码产生了内存泄漏的时候,我们就需要进行内存状态比较. 在可疑的代码段的前后设置内存检查点,比较内存使用是否有可疑的变化.以确定内存是否有泄漏.为此要先定义三个_CrtMemState 对象来保存要比较的内存状态.两个是用来比较,一个用了保存前面两个之间的区别
_CrtMemState Sh1,Sh2,Sh_Diff;
char *pstr1 = new char[100];
_CrtMemCheckPoint(&Sh1); ->设置第一个内存检查点
char *pstr2 = new char[100];
_CrtMemCheckPoint(&Sh2); ->设置第二个内存检查点
_CrtMemDifference(&Sh_Diff, &Sh1, &Sh2); ->检查变化
_CrtMemDumpAllObjectsSince(&Sh_Diff); ->Dump变化