zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Windows下C++软件调试——检测内存泄露

2023-09-11 14:22:29 时间

前言

之前在面一家公司的时候被问及到:当你接收同事的项目,项目之中可能存在内存泄露,而且代码能够正常的运行不会报错。在这种情况下找出该项目中可能存在的内存泄露问题(比如原同事只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();
}
输出结果:

其它的函数也是大同小异,这里就不一一举例了。