使用CRT调试内存分配堆来找出未释放的内存空间

忘记释放已经分配的内存是一种常见的编程错误,当然我指的是在 C++ 编程当中,例如下面的代码里面就存在一个忘记释放内存的编程错误。我个人觉得忘记释放内存的编程错误是不可避免的,毕竟程序员都是人,困了,心情不好了,代码过于复杂啦等等都可能导致忘记加上一句 delete XXX 语句。

// 未释放内存空间 .cpp : Defines the entry point for the console application.

 

#include "stdafx.h"

#include <windows.h>

#include <string>

#include <iostream>

 

using namespace std;

 

class CTestClass

{

public :

    CTestClass(LPWSTR szName)

    {

        m_lpName = new wstring(szName);

    }

 

    ~CTestClass()

    {

    }

 

    void PrintName()

    {

        wcout << *m_lpName << endl;

    }

 

private :

    wstring *m_lpName;

};

 

HRESULT CreateTestClass(LPWSTR szName, CTestClass **ppObject)

{

    *ppObject = new CTestClass(szName);

    if ( (*ppObject) == NULL )

        return E_FAIL;

    else

        return S_OK;

}

 

int _tmain(int argc, _TCHAR* argv[])

{

    CTestClass *pObject = NULL;

    HRESULT hr = CreateTestClass(L"This is a Test" , &pObject);

    if ( hr != S_OK )

    {

        return -1;

    }

    else

    {

        pObject->PrintName();  // pObject 没有被释放

        return 0;

    }

}

 

实际上 Visual Studio 已经提供了方法帮助你快速找到这些没有释放的内存。

 

Visual Studio 提供了一系列的 CRT 调试 API CRT 提供了一个调试内存分配堆,可以跟踪和管理内存在什么地方分配,当你在这个堆上分配内存的时候,每一次内存分配调用例如 malloc 或者 new CRT 都会额外分配大约 36 个字节用来保存例如这个内存块分配的文件名、行号、内存块的大小等信息,最后 CRT 将这些内存块使用一个双链表链接起来。每一次内存释放的时候, free 或者 delete 函数就从这个内存块链表里面将要释放的内存块删除,因此在需要检查内存泄漏的时候,只要遍历这个双链表依次打印出这些内存块就可以发现所有未释放的内存了。下面是 CRT 内存块的原始声明:

typedef struct _CrtMemBlockHeader

{

    // Pointer to the block allocated just before this one:

    struct _CrtMemBlockHeader *pBlockHeaderNext;

    // Pointer to the block allocated just after this one:

    struct _CrtMemBlockHeader *pBlockHeaderPrev;

    char *szFileName;    // File name

    int nLine;           // Line number

    size_t nDataSize;    // Size of user block

    int nBlockUse;       // Type of block

    long lRequest;       // Allocation number

    // Buffer just before (lower than) the user's memory:

    unsigned char gap[nNoMansLandSize];

} _CrtMemBlockHeader;

 

下面的代码演示了如何使用 CRT 提供的调试 API 来修改刚才的源文件检测未释放的内存空间(注意红色添加的部分):

// 未释放内存空间 .cpp : Defines the entry point for the console application.

//

 

#include "stdafx.h"

#include <windows.h>

#include <string>

#include <iostream>

// 使用 CRT 调试 API

#include <crtdbg.h>

 

using namespace std;

 

// 将所有的内存分配函数 new 替换成 CRT 提供的调试 new

#ifdef _DEBUG

#   define DEBUG_CLIENTBLOCK new (_CLIENT_BLOCK, __FILE__, __LINE__)

#else

#   define DEBUG_CLIENTBLOCK

#endif

 

#ifdef _DEBUG

#    define new DEBUG_CLIENTBLOCK

#endif

 

class CTestClass

{

public :

    CTestClass(LPWSTR szName)

    {

        m_lpName = new wstring(szName);

    }

 

    ~CTestClass()

    {

    }

 

    void PrintName()

    {

        wcout << *m_lpName << endl;

    }

 

private :

    wstring *m_lpName;

};

 

HRESULT CreateTestClass(LPWSTR szName, CTestClass **ppObject)

{

    *ppObject = new CTestClass(szName);

    if ( (*ppObject) == NULL )

        return E_FAIL;

    else

        return S_OK;

}

 

int _tmain(int argc, _TCHAR* argv[])

{  

// 设置 CRT 调试 API 的报表输出模式,将所有的错误、警告还有断言都输出到控制台

    _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );

    _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );

    _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );

    _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );

    _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );

    _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );

 

    CTestClass *pObject = NULL;

    HRESULT hr = CreateTestClass(L"This is a Test" , &pObject);

    if ( hr != S_OK )

    {

        return -1;

    }

    else

    {

        pObject->PrintName();

 

// 检查未释放的内存

        _CrtDumpMemoryLeaks();

        return 0;

    }

}

 

这里面是我们的输出结果:

 

使用CRT调试内存分配堆来找出未释放的内存空间_第1张图片

从上面的输出我们可以看出,在“未释放内存空间 .cpp ”的第 27 行和第 45 行所分配的内存没有被适当释放,查看源代码可以发现就是 CTestClass CTestClass 的成员变量 m_Name 没有被释放。

 

http://blog.csdn.net/Donjuan/archive/2009/02/02/3859154.aspx

你可能感兴趣的:(使用CRT调试内存分配堆来找出未释放的内存空间)