#define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h>
注:#include 语句必须采用上文所示顺序。如果更改了顺序,所使用的函数可能无法正确工作。 到这里,对malloc等的调用,都会被记录下来,但运行一遍程序,会发现,并没有打印任何泄漏信息。这是因为目前为止,只记录了内存分配,并没有输出信息。 输出记录的未释放内存,是通过调用_CrtDumpMemoryLeaks();实现。_CrtDumpMemoryLeaks的作用就是收集所有未释放的内存信息,并打印出来。所以,通常_CrtDumpMemoryLeaks要放到程序结束的位置。但一个程序往往有多个结束的位置,并且,_CrtDumpMemoryLeaks打印的是执行_CrtDumpMemoryLeaks的时候,未释放的内存。有些内存,往往在_CrtDumpMemoryLeaks之后释放,_CrtDumpMemoryLeaks仍然会报告出泄漏。
解决方法是:在程序开始处调用: _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); 该语句在程序退出时自动调用 _CrtDumpMemoryLeaks,我们不需要再调用_CrtDumpMemoryLeaks打印报告了。
进阶篇: 在_CrtDumpMemoryLeaks中加断点发现,当调用了_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF)之后,vc程序会在__DllMainCRTStartup函数中处理(dwReason == DLL_PROCESS_DETACH)分支时,调用_CRT_INIT,而_CRT_INIT内部,调用了_CrtDumpMemoryLeaks。
1.2 对于C++分配的内存 #ifdef _DEBUG #define new new(_NORMAL_BLOCK, FILE, LINE) #endif
通过宏定义,把对new的调用,映射到带文件名和行号参数的operator new函数上,并最终调用到_heap_alloc_dbg_impl,并把分配内存的文件名、行号传递给_heap_alloc_dbg_impl。
注:_NORMAL_BLOCK也是在#include <crtdbg.h>中定义。并且C++分配的内存,也需要调用_CrtDumpMemoryLeaks打印报告(可通过程序入口出调用_CrtSetDbgFlag来避免对_CrtDumpMemoryLeaks的直接调用)。
二、对于MFC程序 MFC工程,最终也是按Win32工程方式的内存泄漏执行,不过MFC工程创建向导生成的MFC工程,自动支持C++分配的内存泄漏检测,我们不需要任何处理。
2.1 对于C++分配的内存 MFC是通过下面的语句支持的: #ifdef _DEBUG #define new DEBUG_NEW #endif
我们简单分析一下: 在MFC中,DEBUG_NEW也是个宏,定义为: #define DEBUG_NEW new(THIS_FILE, LINE) 所以,在MFC debug下,调用new操作符,会使用 void* __cdecl operator new(size_t nSize, LPCSTR lpszFileName, int nLine) 来分配内存,而operator new最终会调用到_heap_alloc_dbg_impl,并把分配内存的文件名、行号传递给_heap_alloc_dbg_impl。 当我们调用delete删除内存时,operator delete函数会最终执行_free_dbg_nolock,而_free_dbg_nolock内部,会把待删除的指针的记录,从_pFirstBlock链表中删除。 当程序结束时,_pFirstBlock会检测链表中未删除的内存,给出内存泄漏报告。
需要注意的是,并不是每个cpp文件中,都定义了DEBUG_NEW,尤其后添加的文件。
2.2 对于C分配的内存。 默认不显示文件名和行号。我们最终的目的,是让对malloc等函数的调用,调用到_malloc_dbg等函数上(因为只有_malloc_dbg才接收带文件名和行号的参数)。而<crtdbg.h>中,已把malloc映射到_malloc_dbg上了。但MFC程序,也做了对malloc的映射。所以,我们只要在MFC程序的stdafx.h文件中、在 #include <afxwin.h> // MFC core and standard components 之前,包含下面的代码即可: #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h>
注意,这些映射有_CRTDBG_MAP_ALLOC这个条件宏,所以要先定义它。
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
//或者在进程入口处包含_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
//进程出口处包含 _CrtDumpMemoryLeaks();
//即可检测到内存泄露
//以如下测试函数为例:
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
char* pChars = new char[10];
int*p = new int;
// _CrtDumpMemoryLeaks();
return 0;
}