1. 程式人生 > >Windows下C++軟體除錯——檢測記憶體洩露

Windows下C++軟體除錯——檢測記憶體洩露

前言

之前在面一家公司的時候被問及到:當你接收同事的專案,專案之中可能存在記憶體洩露,而且程式碼能夠正常的執行不會報錯。在這種情況下找出該專案中可能存在的記憶體洩露問題(比如原同事只new了但是沒有delete),該如何進行檢測。這樣的問題確實很實用,因為在軟體編寫的過程中不僅僅需要完成軟體功能,更加重要的是程式碼的健壯性。

至於Linux平臺下的記憶體洩露檢測在這篇部落格裡面進行了介紹。

1. Win32平臺下

對於堆上記憶體操作(new,malloc)的檢測,可以使用在Win32平臺使用_CrtDumpMemoryLeaks()函式對記憶體洩露進行檢測,實現的程式碼如下

// MemoryLeak.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
#include <crtdbg.h>

using std::cout;
using std::endl;

#define _CRTDBG_MAP_ALLOC

int _tmain(int argc, _TCHAR* argv[])
{
	system("color f0");
	int *p_array = new int[10];
	_CrtDumpMemoryLeaks();	//檢測記憶體洩露

	system("pause");
	return 0;
}

輸出結果:


注意:_CrtDumpMemoryLeaks()函式在上面程式碼的使用中需要寫在函式的末尾,這樣才能夠檢測到。

如果要精確定位記憶體洩露的地方的話就需要對自己定義new操作符,實現的程式碼如下
// MemoryLeak.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
#include <crtdbg.h>

using std::cout;
using std::endl;

#define _CRTDBG_MAP_ALLOC
#define My_new new(_NORMAL_BLOCK, __FILE__, __LINE__)	//定義一個自定義的new

int _tmain(int argc, _TCHAR* argv[])
{
	system("color f0");
	int *p_array = My_new int[10];

	_CrtDumpMemoryLeaks();	//檢測記憶體洩露

	system("pause");
	return 0;
}

輸出結果:


如果想要不將_CrtDumpMemoryLeaks()函式放在尾部,就需要提前做好配置,程式碼為(參考資料(中文))

// MemoryLeak.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
#include <crtdbg.h>

using std::cout;
using std::endl;

#define _CRTDBG_MAP_ALLOC
#define My_new new(_NORMAL_BLOCK, __FILE__, __LINE__)	//定義一個自定義的new

int _tmain(int argc, _TCHAR* argv[])
{
	_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)|_CRTDBG_LEAK_CHECK_DF);
	system("color f0");
	int *p_array = My_new int[10];

	system("pause");
	return 0;
}
輸出結果:

_CrtDumpMemoryLeaks一般都在有懷疑是記憶體洩漏的程式碼後面呼叫,除了這個函式之外使用率會比較高的函式是_CrtMemCheckpoint, 設定一個記憶體檢查點。這個函式會取得當前記憶體的執行狀態;_CrtMemDifference,檢查兩種記憶體狀態的異同; _CrtMemDumpAllObjectsSince,從程式執行開始,或者從某個記憶體檢查點開始Dump出堆中物件的資訊;還有就是_CrtDumpMemoryLeaks當發生記憶體溢位的時候Dump出堆中的記憶體資訊 。

// MemoryLeak.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
#include <crtdbg.h>
#include <errno.h>

using std::cout;
using std::endl;

#define _CRTDBG_MAP_ALLOC
#define My_new new(_NORMAL_BLOCK, __FILE__, __LINE__)	//定義一個自定義的new

int _tmain(int argc, _TCHAR* argv[])
{
	system("color f0");
	_CrtMemState Sh1, Sh2, Sh_Diff;
	int* p_array1 = My_new int[10];
	_CrtMemCheckpoint(&Sh1);					//設定第一個記憶體檢查點
	int* p_array2 = My_new int[10];
	memset(p_array2, 0, sizeof(int)*10);
	_CrtMemCheckpoint(&Sh2);					//設定第二個記憶體檢查點
	_CrtMemDifference(&Sh_Diff, &Sh1, &Sh2);	//檢查變化
	_CrtMemDumpAllObjectsSince(&Sh_Diff);		//Dump變化

	system("pause");
	return 0;
}

輸出結果:

2. MFC環境平臺下

Debug版本的MFC本身就提供一部分的記憶體洩漏檢查。 大部分的new 和delete沒有配對使用而產生的記憶體洩漏,MFC都會產生報告。這個主要是因為MFC過載了Debug版本的new 和delete操作符, 並且對前面提到的API函式重新進行了包裝。在MFC類庫中檢查記憶體洩漏的Class就叫 CMemoryState,它重新包裝了了_CrtMemState,_CrtMemCheckPoint, _CrtMemDifference, _CrtMemDumpAllObjectsSince這些函式。並對於其他的函式提供了Afx開頭的函式,供MFC程式使用。比如 AfxCheckMemory, AfxDumpMemoryLeaks 這些函式的基本用法同上面提到的差不多。 CMemoryState和相關的函式的定義都在Afx.h這個標頭檔案中。
一般在*.cpp檔案中會出現下面幾行程式碼

#ifdef _DEBUG
#define new DEBUG_NEW
#endif
這是對DEBUG_NEW的定義
#define DEBUG_NEW new(THIS_FILE, __LINE__)
相當於MFC已經包裝好了,只需要使用就好了。下面是我寫的一個測試用例
void CFile_TransDlg::OnBnClickedButtonTest()
{
	// TODO:  在此新增控制元件通知處理程式程式碼
	int* p_array = new int[10];

	AfxDumpMemoryLeaks();
}
輸出結果: 其它的函式也是大同小異,這裡就不一一舉例了。