/***************************************************************************************************************************/
动态分配内存是编程中容易出现故障的原因之一,其中最常见的故障可能就是内存泄露。例如,用了new操作符分配内存,当不需要是却没有delete将其释放,那么将出现内存泄露的故障。
对于这个问题,vs2010中提供了很多诊断的方法,他们使用特殊的调试版本的空闲存储器。这些方法是在crtdbg.h的头文件中声明的,在发布版本中自动删除这些方法。
下面将介绍一些特殊的检查空闲存储器的函数,而这些函数将要检查的是在_CrtMemState类型结构体中定义的变量(存储内存状态的变量):
typedef struct _CrtMemState
{
struct _CrtMemBlockHeader* pBlockHeader; //指向最近分配的内存块的指针;
unsigned long lCounts [_MAx_BLOCK]; //每个类型内存块的计数器;
unsigned long lSize[_MAx_BLOCK]; //每个内存块中分配的大小;
unsigned lng lHighWaterCount; //至今一次分配的最大字节数;
unsigned long lTotalCount; //目前一共分配的多少字节;
} _CrtMemState;
/*************************************************************************************************************************/
(1)第一个函数的声明: void _CrtMemCheckpoint (_CrtMemState* state);
此函数就是把空闲存储器当前状态,存储到state指针指向的那个_CrtMemState结构体中;
(2)第二个函数: int _CrtMemDifference ( _CrtMemState* stateDiff, const _CrtMemState* oldState, const _CrtMemState* newState);
此函数就是比较两个实参oldState和newState的差别,把结构存到第一个实参stateDiff中。函数的返回表明了比较的结果:非0,就是状态不同;0,表示状态一致;
(3)第三个函数:void _CrtMemDumpAllObjectsSince( const _CrtMemState* state);
此函数就是输出(非标准的stdout)上次快照或程序开始执行以来在堆中分配的所有对象的 信息; 若传入的实参为空指针,那么就是输出自程序子开始执行以来,所有已分配的对象的有关信息;
(4)第四个函数: void _CrtMemDumpStatistics( const _CrtMemState* state);
此函数就是将实参指定的空闲存储器状态的有关信息转存到输出流中。实参指向的状态结构可以使用_CrtMemCheckpoint()函数拍照,也可用_CrtMemDifference函数来比较两个状态的差别;
(5)第五个函数: int _CrtDumpMemoryLeaks();
此函数就是检查内存泄露的情况,并转储(即转储到“输出流”)与任何泄露有关的信息。 在任何时刻都可以调用该函数,但有一种机制,可以再程序结束时自动调用该程序。下面将看到如何启用该机制:
/*************************************************************************************************************************/
我们可以设置int类型的标志_CrtDbgFlag来控制空闲存储器的调试操作,该标志有五个控制位:
_CRTDBG_ALLOC_MEM_DF -----------------------------为1时,启用调试地址分配的功能,可以跟踪空闲存储器的状态;默认情况下,为1;
_CRTDBG_DELAY_FREE_MEM_DF -----------------------------为1时,将阻止delete释放被分配的内存,这样可以确定在低内存条件下发生什么事;
_CRTDBG_CHECK_ALWAYS_DF ------------------------------为1时,将使得每次使用new和delete操作符时都自动调用_CrtCheckMemory()函数 ,该函数 检查空闲存储器的完整性,如有没有越界写内存,如果有将输出一份报告。
_CRTDBG_CHECK_CRT_DF ---------------------------为1时,调试器将跟踪有内部运行库使用的内存;
_CRTDBG_LEAK_CHECK_DF ---------------------------为1时,将在程序结束时自动调用_CrtDumpMemoryLeaks() 函数,来执行内存泄露检查。只有 在为释放完所有已分配的内存时,才会有该函数的输出;
默认情况下,只有第一个为1,其余的都为0;
/*************************************************************************************************************************/
为了设置标志,我们使用下面方法:
int flag= _CrtSetDbgFlag (_CRTDBG_REPORT_FLAG) //获取当前设置的标志,为flag
然后,要设置相应的位,就用flag与或相应标志位来设置标志,最后将flag在传给函数
_CrtSetDbgFlag(flag);
/*************************************************************************************************************************/
空闲存储器调试函数的输出目的地不是标准输出流,默认情况下该目的地是调试消息窗口。如果希望在stdout上看到输出,可以进行相应的设置:
int _CrtSetReportMode(int reportType, int reportMode);
该函数有3类输出,对于由第一个实参指定的输出类型,由第二个实参指定输出目的地;输出类型分为3类:
_CRT_WARN ------------------------------------------------各种警告信息,检查到内存泄露时的输出就是警告信息;
_CRT_ERROR -----------------------------------------------报告不可恢复问题的灾难性错误信息;
_CRT_ASSERT ------------------------------------------------来自断言的输出(不是assert()函数的输出)
对于输出的目的地如下类型:
_CRTDBG_MODE_DEBUG ------------------ This is the default mode, which sends output to a debug string that you
see in the Debug window when running under control of the debugger.
_CRTDBG_MODE_FILE ------------------ Output is to be directed to an output stream.
_CRTDBG_MODE_WNDW ------------------ Output is presented in a message box.
_CRTDBG_REPORT_MODE ------------------ if you specify this, the _CrtSetReportMode() function just returns the
current report mode.
为了指定多个目的地,,可以使用或|运算符来表示即可。对于内存泄露的检查,可以将输出定向到某个文件流:
_CrtSetReportMode(_CRT_WARN, _CRT_MODE_FILE);
同时,还要调用_CrtSetReportFile()函数来 指定具体的目的地:
_HFILE _CrtSetReportFile( int reportType, _HFILE reportFile);
这里的第二个参数,指向_HFILE类型的文件流指针,也可以是下面的标识之一:
_CRTDBG_FILE_STDERR ---------------------------------将输出定向到stderr
_CRTDBG_FILE_STDOUT ---------------------------------将输出定向的stdout
_CRTDBG_REPORT_FILE ---------------------------------该函数仅仅返回当前的目的地
当调试输出量很大时,我们希望输出到文件中,这时可以通过将输出流定向到文件,使用下面函数:
errno_t freopen_s( File** pFile, const char* filepath, const char* mode, File* stream);
给出一个将stdout重定向到文件名为debug_out.txt的文件:
FILE * pOut(nullptr);
errno_t err= freopen_s(&pOut, "debug_out.txt", "w", stdout);
if(err)
cout<< "error on freopen"<<endl;
else
cout<< "successfully reassigned" <<endl;