使用 CRT 库查找内存泄漏

发布于 2022-01-19


编写 C/C++ 程序需要关注代码逻辑是否存在内存泄漏。Visual Stdio C++ 的 CRT 库自带了检测内存泄漏的工具。

首先在程序的开头按照以下的顺序包含以下代码:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#define _CRTDBG_MAP_ALLOC 这行代码会把 CRT 里面分配和释放内存相关的函数映射转成成对于的调试版。例如 malloc 映射转成 _malloc_dbgfree 映射转成 _free_dbg。调试版函数除了分配和释放内存,还做一些记录的相关工作。

然后在程序退出是调用 _CrtDumpMemoryLeaks(),这样就会输出检测内存泄漏的结果。下面是个完整的例子:

#define _CRTDBG_MAP_ALLOC

#include <crtdbg.h>
#include <stdlib.h>

int main() {
  int *arr = (int *)malloc(sizeof(int) * 100);

  _CrtDumpMemoryLeaks();
}

输出结果:
Detected memory leaks!
Dumping objects ->
ConsoleApplication25.cpp(9) : {88} normal block at 0x007F4BF0, 400 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

使用起来很简单。

注意:如果我们在多个源文件代码中分配了内存,那么每个源文件都要加上:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

否则的话,则无法检测到泄漏内存的具体源文件和行数。

另外,一般复杂的程序有多个程序退出口,有时候我们无法找到一个合适的地方调用 _CrtDumpMemoryLeaks()。因此还有另外一种写法:

#define _CRTDBG_MAP_ALLOC

#include <crtdbg.h>
#include <stdlib.h>

#include "aaa.h"

int main() {
  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);

  int *arr = (int *)malloc(sizeof(int) * 100);
}

即在程序的开头加上:

  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);

程序结束后会自动输出检测内存泄漏的结果。

C++ 分配和释放内存使用的是 new 和 delete。我们还需要做一些工作:

#define _CRTDBG_MAP_ALLOC

#include <crtdbg.h>
#include <stdlib.h>

#include <string>

#define DEBUG_NEW new(_NORMAL_BLOCK ,__FILE__, __LINE__)
#define new DEBUG_NEW

int main() {
  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);

  int *arr = (int *)malloc(sizeof(int) * 100);

  std::string* s1 = new std::string("123456789");
}

输出结果:
Detected memory leaks!
Dumping objects ->
{167} normal block at 0x014E7068, 8 bytes long.
 Data: <` M     > 60 C0 4D 01 00 00 00 00 
ConsoleApplication25.cpp(17) : {166} normal block at 0x014DC060, 28 bytes long.
 Data: <hpN 123456789   > 68 70 4E 01 31 32 33 34 35 36 37 38 39 00 CD CD 
ConsoleApplication25.cpp(15) : {165} normal block at 0x014E7B88, 400 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

参考