windows下C/C++的内存泄露检测
C/C++由于其没有垃圾回收机制,所以内存的释放一直以来都依靠于程序员的手工释放,因此极其容易出现内存泄露的问题,而在比较大的程序之中,查找内存泄露是一件比较困难的事情,所以我们需要一些简便的方法来检测内存泄露,避免内存泄露导致设备崩溃。
检测方法
利用Visual Studio调试器和CRT库提供的检测(malloc和new均适用)
检测内存泄露
程序只从单一位置退出时:
1.需要以下头文件:
#define _CRTDBG_MAP_ALLOC
#include<stdlib.h>
#include<crtdbg.h>
通过包含crtdbg.h
,将malloc和free分别映射到_malloc_dbg和_free_dbg,用于内存分配和释放的跟踪。
#define _CRTDBG_MAP_ALLOC语句用于提供额外的信息,非绝对必要。
2.在程序退出位置前使用以下语句:
_CrtDumpMemoryLeaks();
该语句会在输出窗口显示内存泄露信息。
测试代码
#define _CRTDBG_MAP_ALLOC
#include<stdio.h>
#include<stdlib.h>
#include<crtdbg.h>
#define NUM 10
int main()
{
char *test;
test = (char*)malloc(NUM * sizeof(char));
_CrtDumpMemoryLeaks();
return 0;
}
输出结果
使用_CRTDBG_MAP_ALLOC
时:
未使用_CRTDBG_MAP_ALLOC
时:
如果将程序中的:
char *test;
test = (char*)malloc(NUM * sizeof(char));
替换为:
char* name = new char[10];
也会得到同样的结果(内存位置由于自动分配有所不同):
程序从多个位置退出时:
1.同样包含上述头文件
2.在每个会退出程序的函数(包括main函数)开始处包含以下代码:
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
程序会在退出时自动调用_CrtDumpMemoryLeaks();
。
测试代码
#define _CRTDBG_MAP_ALLOC
#include<stdio.h>
#include<stdlib.h>
#include<crtdbg.h>
#define NUM 10
void test1();
void test2();
int main()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
char *test;
test = (char*)malloc(NUM * sizeof(char));
int a;
puts("input a number");
scanf_s("%d", &a, sizeof(int));
if (a > 10)
test1();
else if(a<20)
test2();
else
puts("exit point 3");
return 0;
}
void test1()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
puts("exit point 1");
exit(EXIT_SUCCESS);
}
void test2()
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
puts("exit point 2");
exit(EXIT_SUCCESS);
}
输出结果
定位内存泄露
当程序不大时,内存泄露位置可以轻易通过肉眼分辨或者代码审计发现。但是当程序较大的时候,就难以发现具体发生内存泄露的位置。此时可以通过CRT库的_CrtMenState
结构储存内存状态。
使用该结构类型需要定义变量用于储存内存状态:
_CrtMenState s1,s2,s3;
检测指定位置内存需要用到_CrtMenCheckPoint
结构:
_CrtMenCheckPoint(&s1);
该语句将当前位置的内存状态传递到_CrtMenState
结构变量s1中。
检测两个位置之间的内存状态需要用到_CrtMenDifference()
函数,同时需要一个新的_CrtMenState
结构变量用于存储状态之间的差异:
_CrtMemCheckpoint( &s1 );
_CrtMemCheckpoint( &s2 );
_CrtMemDifference( &s3, &s1, &s2);
转储_CrtMenState
结构的内容需要用到_CrtMemDumpStatistics
函数:
_CrtMenDumpStatics(&s3);
测试代码
#define _CRTDBG_MAP_ALLOC
#include<stdio.h>
#include<stdlib.h>
#include<crtdbg.h>
#define NUM 10
_CrtMemState s1, s2, s3;
void MEM(char *str, int n);
int main()
{
_CrtMemCheckpoint(&s1);
char *test=NULL;
MEM(test, NUM);
_CrtMemCheckpoint(&s2);
puts("test
");
_CrtMemDifference(&s3, &s1, &s2);
_CrtMemDumpStatistics(&s3);
return 0;
}
void MEM(char *str, int n)
{
str = (char*)malloc(n * sizeof(char));
}
输出结果
当s1和s2之间调用了函数MEN()
时:
当删去程序中的
MEN(test, NUM);
即s1和s2之间没有调用MEN()
函数时:
如果单纯使用_CrtDumpMemoryLeaks();
,只能显示内存分配的位置,即函数MEN()
中分配内存语句的位置,而不能定位到是哪一次对函数MEN()
调用导致的内存泄露,而通过结构_CrtMenState
,可以得到语句之间是否存在内存分配,从而定位出内存泄露的位置。
总结
内存泄露的原因很简单,无非就是分配了内存而没有释放,为了解决这个问题,除了养成良好的习惯,还有要善用工具在编写程序的时候实时监测是否出现内存泄露,减少后期排bug时的工作量。
============ End