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\n");
_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
