Heim  >  Artikel  >  Datenbank  >  vc++ 调试技巧整理

vc++ 调试技巧整理

WBOY
WBOYOriginal
2016-06-07 15:00:001060Durchsuche

VC调试技巧收集整理 调试是一个程序员最基本的技能,其重要性甚至超过学习一门语言。不会调试的程序员就意味着他即使会一门语言,却不能编制出任何好的软件。 这里我简要的根据自己的经验列出调试中比较常用的技巧,希望对大家有用。本文约定,在选择菜单时



VC调试技巧收集整理 
      调试是一个程序员最基本的技能,其重要性甚至超过学习一门语言。不会调试的程序员就意味着他即使会一门语言,却不能编制出任何好的软件。
      这里我简要的根据自己的经验列出调试中比较常用的技巧,希望对大家有用。本文约定,在选择菜单时,通过/表示分级菜单,例如File/Open表示顶级菜单File的子菜单open。        
        
        1 设置
        为了调试一个程序,首先必须使程序中包含调试信息。一般情况下,一个从AppWizard创建的工程中包含的Debug   Configuration自动包含调试信息,但是是不是Debug版本并不是程序包含调试信息的决定因素,程序设计者可以在任意的Configuration中增加调试信息,包括Release版本。
        为了增加调试信息,可以按照下述步骤进行:

        
        a 打开Project settings对话框(可以通过快捷键ALT+F7打开,也可以通过IDE菜单Project/Settings打开)         
        b 选择C/C++页,Category中选择general ,则出现一个Debug Info下拉列表框,可供选择的调试信息方式包括: 
         None:
         没有调试信息 
         Line Numbers Only:
         目标文件或者可执行文件中只包含全局和导出符号以及代码行信息,不包含符号调试信息 
         C7 Compatible: 
         目标文件或者可执行文件中包含行号和所有符号调试信息,包括变量名及类型.函数及原型 等 
         Program Database: 
         创建一个程序库(PDB),包括类型信息和符号调试信息。 
         Program Database for Edit and Continue: 
         除了上面的功能外,这个选项允许对代码进行调试过程中的修改和继续执行。
         这个选项同时使#pragma设置的优化功能无效 
        
        c 选择Link页,选中复选框"Generate Debug Info",这个选项将使连接器把调试信息写进可执行文件和DLL 
      .如果C/C++页中设置了Program Database以上的选项,则Link  incrementally可以选择。选中这个选项,将使程序可以在上一次编译的基础上被编译(即增量编译),而不必每次都从头开始编译。        
        
        2 断点
        断点是调试器设置的一个代码位置。当程序运行到断点时,程序中断执行,回到调试器。断点是最常用的技巧。调试时,只有设置了断点并使程序回到调试器,才能对程序进行在线调试。        
        设置断点:可以通过下述方法设置一个断点。首先把光标移动到需要设置断点的代码行上,然后 
        按F9快捷键 
        弹出Breakpoints对话框,方法是按快捷键CTRL+B或ALT+F9,或者通过菜单Edit/Breakpoints打开。打开后点击Break 
      at编辑框的右侧的箭头,选择 合适的位置信息。一般情况下,直接选择line 
      xxx就足够了,如果想设置不是当前位置的断点,可以选择Advanced,然后填写函数、行号和可执行文件信息。 
        去掉断点:把光标移动到给定断点所在的行,再次按F9就可以取消断点。同前面所述,打开Breakpoints对话框后,也可以按照界面提示去掉断点。        
        条件断点:可以为断点设置一个条件,这样的断点称为条件断点。对于新加的断点,可以单击Conditions按钮,为断点设置一个表达式。当这个表达式发生改变时,程序就被中断。底下设置包括“观察数组或者结构的元素个数”,似乎可以设置一个指针所指向的内存区的大小,但是我设置一个比较的值但是改动 
      范围之外的内存区似乎也导致断点起效。最后一个设置可以让程序先执行多少次然后才到达断点。
        
        数据断点:数据断点只能在Breakpoints对话框中设置。选择“Data”页,就显示了设置数据断点的对话框。在编辑框中输入一个表达式,当这个 
      表达式的值发生变化时,数据断点就到达。一般情况下,这个表达式应该由运算符和全局变量构成,例如:在编辑框中输入 
      g_bFlag这个全局变量的名字,那么当程序中有g_bFlag= !g_bFlag时,程序就将停在这个语句处。
        
        消息断点:VC也支持对Windows消息进行截获。他有两种方式进行截获:窗口消息处理函数和特定消息中断。在Breakpoints对话框中选择Messages页,就可以设置消息断点。如果在上面那个对话框中写入消息处理函数的名字,那么每次消息被这个函数处理,断点就到达(我觉得如果采用普通断点在这个函数中截获,效果应该一样)。如果在底下的下拉 
      列表框选择一个消息,则每次这种消息到达,程序就中断。
        
        
        3 Watch
        VC支持查看变量、表达式和内存的值。所有这些观察都必须是在断点中断的情况下进行。
        观看变量的值最简单,当断点到达时,把光标移动到这个变量上,停留一会就可以看到变量的值。
        VC提供一种被成为Watch的机制来观看变量和表达式的值。在断点状态下,在变量上单击右键,选择Quick Watch, 
      就弹出一个对话框,显示这个变量的值。
        单击Debug工具条上的Watch按钮,就出现一个Watch视图(Watch1,Watch2,Watch3,Watch4),在该视图中输入变量或者表达式,就可以观察 
      变量或者表达式的值。注意:这个表达式不能有副作用,例如++运算符绝对禁止用于这个表达式中,因为这个运算符将修改变量的值,导致 软件的逻辑被破坏。
        
        
        4 Memory
        由于指针指向的数组,Watch只能显示第一个元素的值。为了显示数组的后续内容,或者要显示一片内存的内容,可以使用memory功能。在 
      Debug工具条上点memory按钮,就弹出一个对话框,在其中输入地址,就可以显示该地址指向的内存的内容。
        
        
        5 Varibles
        Debug工具条上的Varibles按钮弹出一个框,显示所有当前执行上下文中可见的变量的值。特别是当前指令涉及的变量,以红色显示。
        
        
        6 寄存器
        Debug工具条上的Reigsters按钮弹出一个框,显示当前的所有寄存器的值。
        
        
        7 进程控制
        VC允许被中断的程序继续运行、单步运行和运行到指定光标处,分别对应快捷键F5、F10/F11和CTRL+F10。各个快捷键功能如下: 
        快捷键 说明 
        F5 继续运行 
        F10 单步,如果涉及到子函数,不进入子函数内部 
        F11 单步,如果涉及到子函数,进入子函数内部 
        CTRL+F10 运行到当前光标处。 
        
        
        8 Call Stack
        调用堆栈反映了当前断点处函数是被那些函数按照什么顺序调用的。单击Debug工具条上的Call stack就显示Call 
      Stack对话框。在CallStack对话框中显示了一个调用系列,最上面的是当前函数,往下依次是调用函数的上级函数。单击这些函数名可以跳到对应的函数中去。
        
        
        9 其他调试手段
        系统提供一系列特殊的函数或者宏来处理Debug版本相关的信息,如下: 
        
        10 宏名/函数名 说明 
        TRACE 使用方法和printf完全一致,他在output框中输出调试信息 
        ASSERT 它接收一个表达式,如果这个表达式为TRUE,则无动作,否则中断当前程序执行。对于系统中出现这个宏 
      导致的中断,应该认为你的函数调用未能满足系统的调用此函数的前提条件。例如,对于一个还没有创建的窗口调用SetWindowText等。 
        VERIFY 和ASSERT功能类似,所不同的是,在Release版本中,ASSERT不计算输入的表达式的值,而VERIFY计算表达式的值。 
        
        关注:
        一个好的程序员不应该把所有的判断交给编译器和调试器,应该在程序中自己加以程序保护和错误定位,具体措施包括: 
        
         对于所有有返回值的函数,都应该检查返回值,除非你确信这个函数调用绝对不会出错,或者不关心它是否出错。 
         一些函数返回错误,需要用其他函数获得错误的具体信息。例如accept返回INVALID_SOCKET表示accept失败,为了查明 
      具体的失败原因,应该立刻用WSAGetLastError获得错误码,并针对性的解决问题。 
         有些函数通过异常机制抛出错误,应该用TRY-CATCH语句来检查错误 
        程序员对于能处理的错误,应该自己在底层处理,对于不能处理的,应该报告给用户让他们决定怎么处理。如果程序出了异常, 
      却不对返回值和其他机制返回的错误信息进行判断,只能是加大了找错误的难度。 
         另外:VC中要编制程序不应该一开始就写cpp/h文件,而应该首先创建一个合适的工程。因为只有这样,VC才能选择合适的编译、连接 
      选项。对于加入到工程中的cpp文件,应该检查是否在第一行显式的包含stdafx.h头文件,这是Microsoft Visual 
      Studio为了加快编译 速度而设置的预编译头文件。在这个#include 
      "stdafx.h"行前面的所有代码将被忽略,所以其他头文件应该在这一行后面被包含。
         对于.c文件,由于不能包含stdafx.h,因此可以通过Project settings把它的预编译头设置为“不使用”,方法是: 
        弹出Project settings对话框 
        选择C/C++ 
        Category选择Precompilation Header 
        选择不使用预编译头。
      [url]http://www.yzcc.com/yzcc/vv/08475592434.html[/url]

      其他技巧:

      1.在调试状态下怎样查看错误消息(GetLastError())?
      通常可以用GetLastError()得到错误编号然后用FormatMessage(...)得到错误描述。
      这里有一个更直接的办法:在Watch窗口添加@err,hr

      2.怎样知道程序是否有内存泄漏(Memory Leak)?
      在VC开发环境下Press 
      [F5],在调试状态下运行程序,测试有可能出现内存泄漏的操作,关闭程序,在Output窗口查看运行信息.如果出现泄漏,在Output中会有记录。当然,不能完全依靠这种方式来发现程序运行有内存泄漏。

      3.当某一变量满足某种条件时,停止在断点.
      如以下一程序片段:
      2 int iLocation;
      ...
      30 iLocation++
      ...
      要求: 在line30设有断点,并想在iLocation>100 
      本文来源:[url]http://blog.tianya.cn/blogger/post_show.asp?BlogID=237133&PostID=3587659[/url]

   便于调试的代码风格:

不用全局变量
所有变量都要初始化,成员变量在构造函数中初始化
尽量使用const
详尽的注释


VC++编译选项:

总是使用/W4警告级别
在调试版本里总是使用/GZ编译选项,用来发现在Release版本中才有的错误
没有警告的编译:保证在编译后没有任何警告,但是在消除警告前要进行仔细检查


调试方法:

1、使用 Assert(原则:尽量简单)
assert只在debug下生效,release下不会被编译。

例子:
char* strcpy(char* dest,char* source)
{
assert(source!=0);
assert(dest!=0);
char* returnstring = dest;
while((*dest++ = *source++)!= ‘/0’)
{
;
}
return returnstring;

2、防御性的编程

例子:
char* strcpy(char* dest,char* source)
{
if(source == 0)
{
assert(false);
reutrn 0;
}
if(dest == 0)
{
assert(false);
return 0;
}
char* returnstring = dest;
while((*dest++ = *source++)!= ‘/0’)
{
;
}
return returnstring;

3、使用Trace

以下的例子只能在debug中显示,

例子:

a)、TRACECString csTest = “test”;
TRACE(“CString is %s/n”,csTest);
b)、ATLTRACE

c)、afxDump
CTime time = CTime::GetCurrentTime();
#ifdef _DEBUG
afxDump
#endif
4、用GetLastError来检测返回值,通过得到错误代码来分析错误原因

5、把错误信息记录到文件中

异常处理

程序设计时一定要考虑到异常如何处理,当错误发生后,不应简单的报告错误并退出程序,应当尽可能的想办法恢复到出错前的状态或者让程序从头开始运行,并且对于某些错误,应该能够容错,即允许错误的存在,但是程序还是能够正常完成任务。

调试技巧

1、VC++中F5进行调试运行

a)、在output Debug窗口中可以看到用TRACE打印的信息
b)、 Call Stack窗口中能看到程序的调用堆栈

2、当Debug版本运行时发生崩溃,选择retry进行调试,通过看Call Stack分析出错的位置及原因
3、使用映射文件调试

a)、创建映射文件:Project settings中link项,选中Generate mapfile,输出程序代码地址:/MAPINFO: 
LINES,得到引出序号:/MAPINFO: EXPORTS。
b)、程序发布时,应该把所有模块的映射文件都存档。
c)、查看映射文件:见” 通过崩溃地址找出源代码的出错行”文件。

4、可以调试的Release版本

Project settings中C++项的Debug Info选择为Program Database,Link项的Debug中选择Debug 
Info和Microsoft format。

5、查看API的错误码,在watch窗口输入@err可以查看或者@err,hr,其中”,hr”表示错误码的说明。
6、Set Next Statement:该功能可以直接跳转到指定的代码行执行,一般用来测试异常处理的代码。
7、调试内存变量的变化:当内存发生变化时停下来。

常见错误

1、在函数返回的时候程序崩溃的原因

a)、写自动变量越界
b)、函数原型不匹配

2、MFC

a)、使用错误的函数原型处理用户定义消息

正确的函数原型为:
afx_msg LRESULT OnMyMessage(WPARAM wParam,LPARAM lParam);
3、谨慎使用TerminateThread:使用TerminateThread会造成资源泄漏,不到万不得已,不要使用。

4、使用_beginthreadex,不要使用Create Thread来常见线程。

WindowS 调试

1.Windows跟踪语句:

(1)TRACE(_T(“Warning (FunctionName):Object %s not found./n”),objectName);

在输出的调试窗口会输出结果。跟踪信息输出到输出窗口output window中。[调试版本中使用]

(2)C++的C运行时刻库函数跟踪语句
    ANSI C 运行时刻库函数没有跟踪语句,但是VC++的C运行时刻库函数有。可使用_RPTn或_RPTFn调试报告宏,但是你必须在程序中引用crtdbg.h,并利用C运行时刻函数库链接:
   _RPT0(reportType,format);
   _RPT1(reportType,format,arg1);
   _RPT2(reportType,format,arg1,arg2);

   _RPTF0(reportType,format);
   _RPTF1(reportType,format,arg1);
   _RPTF2(reportType,format,arg1,arg2);
   例子:
          int a =1000,b=2000;
         int *p =&a;
         int *q = &b;
       _RPTF2(_CRT_WARN,"%x ,%x",p,q); //直接在outputwindow中输出
       _RPTF2(_CRT_ERROR,"%x ,%x",p,q);   //弹出ERROR对话框
       _RPTF2( _CRT_ASSERT,"%x ,%x",p,q);   //弹出AEESRT对话框
reportType:_CRT_WARN _CRT_ERROR _CRT_ASSERT
其中_CRT_WARN用于跟踪语句,_RPTFn宏报告了源码文件名和调用这些宏的行号。[调试版本中使用]
可以使用_CrtSetReportMode函数改变默认输出设置,(比如输出到调试器输出窗口,文件,消息框中),_CrtSetReportFile可以指定将报告输出到哪个文件中。

(3)MFC中的跟踪语句
       区别:使用TRACE宏时,需要使用_T宏来格式化参数以正确解决Unicode的校正,反之,在TRACEn类型的宏中,不必使用_T宏。
       TRACE(_T("ssss/n"));
       TRACE2("%d r %d l/n",1,2);

(4)异常和返回值

C++程序中可以使用异常和返回值来返回状态信息。C语言返回一个函数的状态的最好方法就是它的返回值
Try
{
//code may fail
}
Catch
{
//handle the failure
}
只有在抛出异常的时候会有开销!Catch块有一些开销,但是try块有很少的开销!只能在调试版中处理异常,并弹出MessageBox,发布版不处理异常,为的就是优化。
     try
     {
       int *pInt = 0;
       *pInt =42;
     }
     catch(...)
     {
       MessageBox(_T("Exception was caught!"),_T("Exception test"),MB_OK);   
     }
      (5)ANSI C++ 运行时刻函数库跟踪
具体有:C语言的stderr和C++语言的clog流,在Windows程序中没有任何效果!

(6)OutputDebugString (_T(“trace   debug info!/n”));[调试版本中使用]
       如果只想在调试版本中使用OutputDebugString,可以使用下面得宏来实现:
#ifdef   _DEBUG
#define OutputTraceString(text)   OutputDebugString(text)
#else
#define OutputTraceString(text)   ((void) (0))
#endif

使用AfxOutputDebugString
AfxOutputDebugString宏使用和OutputDebugString一样的语法。

7.使用CObiect::Dump
CObject类有一个转储(dump)虚拟函数,继承于它的所有子类函数都可以重载这个函数,输出它们的值。
例 1 -2:用下列语句输出CObject派生类pObject的值。afxDump是预定义的全局变量CDumpContext,注意 CDumpContext对最一般的内建数据类型及CObject的指针和引用支持插入操作符(
#if _DEBUG
AfxDump(pObiect);
pObject->Dump(afxDump);
afxDump
#endif

例1-3:
afxDump
8.使用AfxDump
AfxDump是MFC中相当于cerr流的跟踪语句,所以你可以直接向它输出跟踪消息。
TRACE宏由afxDump实现,afxDump由AfxOutputDebugString实现。而AfxOutputDebugString在调试版中由_RPT0宏实现。可以使用下面的方法将afxDump重定向。
#ifdef _DEBUG
CFile dumpFile; //必须为全局变量
     dumpFile.Open(_T("dump.log"),CFile::modeWrite|CFile::modeCreate);
     afxDump.m_pFile = &dumpFile;
#endif
9.使用AfxDumpStack
可以使用AfxDumpStack函数输出一个调用栈:
void AFXAPI AfxDumpStack(DWORD dwTarget = AFX_STACK_DUMP_TARGET_DEFAULT);
参 数:dwTarget决定在调试和发布版本中输出到什么地方,可以输出到TRACE宏,OutputDebugString或到剪贴板。如果使用 AFX_STACK_DUMP_TARGET_TRACE,含义是在调试版中输出到TRACE宏,而在发布版本中没有输出!如果你希望在发布版本中输出, 可使用AFX_STACK_DUMP_TARGET_ODS选项,而且必须在路径中有Imagehlp.dll文件。在project& #61664;settingsLinkCategoryDebug Both formats。 
10.ATL跟踪语句 最基本的类型是AtlTrace函数:
inline void_cdecl AtlTrace(LPCTSTR format……);
ATLTRACE (format……);

和MFC TRACE宏一样,它使用一个512字节固定大小的缓冲区,如果它的参数需要一个大于512字节的文本缓冲区,会导致一个出错的断言。实际上,它是使用API函数OutputDebugString实现的,因此它的输出不能改变到其他目标。
ATL跟踪语句的另一个选择AtlTrace2函数:
Inline void _cedecl AtlTrace2(DWORD category,UINT level,LPCTSTR format,…);
ATLTRACE(category,level,format);
该 函数增加了一个参数跟踪类别(category)(例如,atlTraceCOM,atlTraceWindowing和 atlTraceControl)和一个参数严格级别。类别值是位掩码,ATL自身使用介于0-2的级别值,0级指最严格的级别。 和AtlTrace函数类似,AtlTrace2函数只能用在调试版本中!
11.VC++消息Pragma
Pagama是一个编译时的跟踪语句,可用来警告在预处理过程中发现的潜在的编连(build)问题
12.处理长字符串:[例如处理SQL语句]
(1)MFC下[只能调试版]
TRACE(longString); //assert if _tcslen(longString)>511,最大是512
#ifdef _DEBUG
afxDump
#endif
(2)ATL下[只能调试版]
ATLTRACE(longString); //assert if _tcslen(longString)>511,最大是512
#ifdef _DEBUG
OutputDebugString(longString); //dosen’t assert for long strings 不需要判断
#endif
13.处理大量的跟踪输出。
如果跟踪消息数据产生的速度超过输出窗口处理的速度,那么消息会塞满缓冲区,数据会丢失。
简单方法:在输出大量数据的代码段,例如对象转储函数时候,调用 Sleep API函数。例如:Sleep(100).

 调试
1.当错误发生得时候,调用GetLastError()得到相应得错误码
错误码得位域有固定的格式,,在C:/Program Files/Microsoft Visual Studio/VC98/Include/Winerror.h中有详细的说明:
//
//   Values are 32 bit values layed out as follows:
//
//   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
//   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
//   +---+-+-+-----------------------+-------------------------------+
//   |Sev|C|R|     Facility           |               Code             |
//   +---+-+-+-----------------------+-------------------------------+
//
//       Sev - is the severity code       安全代码
//
//           00 – Success           0-安全   
//           01 – Informational     1-信息
//           10 – Warning         2-警告
//           11 – Error             3-错误
//       C - is the Customer code flag 
客户代码:0-Microsoft定义的,1-客户定义的
//       R - is a reserved bit 保留位必须是0  
//       Facility - is the facility code工具-Microsoft 定义的
//       Code - is the facility's status code 工具状态代码-Microsoft或客户定义
   工具:更好的查看错误代码的方法,Tools-》Error Lookup;在VC++调试器的watch窗口中输入@ERR监视GetLastError的返回数值,ERR是调试器用来显示最新错误码的一个虚拟寄 存器。还可以用 @ERR,hr将错误码转换为文本格式
   
   技巧:在调试中,按F11键,Alt+8显示反汇编窗口,在Edit菜单中选择Go To命令,在GoTo对话框的Go to what中选择Address选项,在Enter address expression中输入导致崩溃的地址。

创 建映射文件的方法:ProjectSettingsLinkGenerate mapfile.即可在工程的Debug文件下看到。映射文件里面所有的公共符号都使用混合名字。可使用VC++的名字解析工具(Undname)将混合 名字转换到原始名字。你可以在“开始””运行””cmd”输入
C:/Documents and Settings/zhangzhongping>cd C:/Program Files/Microsoft Visual Studio/Common/Tools
输入:
C:/ProgramFiles/MicrosoftVisualStudio/Common/Tools>Undname ?RandException@@YGXHHHH@Z
Microsoft(R) Windows NT(R) Operating System
UNDNAME Version 5.00.1768.1Copyright (C) Microsoft Corp. 1981-1998
输出:
>> ?RandException@@YGXHHHH@Z == RandException

当输入-f时,显示整个函数的原型!查看映射文件的时候,若出现重载函数,名字解析工具很有用!
输入:
C:/ProgramFiles/MicrosoftVisualStudio/Common/Tools>Undname-f ?RandException@@YGXHHHH@Z
Microsoft(R) Windows NT(R) Operating System
UNDNAME Version 5.00.1768.1Copyright (C) Microsoft Corp. 1981-1998
输出:
>> ?RandException@@YGXHHHH@Z == void __stdcall RandException(int,int,int,int)

使用MFC和ATL的DEF文件:

MFC的DEF文件在
C:/ProgramFiles/MicrosoftVisual Studio/VC98/MFC/SRC/Intel目录下。
ATL的DEF文件在
C:/Program Files/Microsoft Visual Studio/VC98/ATL/SRC目录下。
如 果用户运行自己的程序出现:The ordinal 6880 could not be located in the dynamic link Library MFC42.dll信息。你可查看MFC42.DEF中对应的6880号函数:? ScreenToClient@CWnd@@QBEXPAUtagRECT@@@Z @ 6880 NONAME.然后可以用名字解析工具解析混合名字如下:
输入:
C:/Program Files/Microsoft Visual Studio/Common/Tools>Undname -f ?ScreenToClient@CWnd@@QBEXPAUtagRECT@@@Z @ 6880 NONAME
Microsoft(R) Windows NT(R) Operating System
UNDNAME Version 5.00.1768.1Copyright (C) Microsoft Corp. 1981-1998
输出:
>> ?ScreenToClient@CWnd@@QBEXPAUtagRECT@@@Z == public: void __thiscall CWnd::ScreenToClient(struct tagRECT *)const
>> @ == @
>> 6880 == 6880
>> NONAME == NONAME
使用依赖关系浏览工具:
VC++依赖关系浏览工具(COMMON/TOOLS/Depends.exe)

 内存泄漏的调试
在VC中我们使用_CrtDumpMemoryLeaks()来检测内存泄漏。
例子:
void CTRACEDlg::OnButton1() 
{
     int *pLeak = new int; //故意造成内存泄漏
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
     //delete pLeak;
     //pLeak = NULL;
     _CrtDumpMemoryLeaks();//调用此函数在output window的Debug窗口中显示内存泄漏的地址的数据。
    
}
Debug窗口中显示:
Detected memory leaks!
Dumping objects ->
C:/ProgramFiles/MicrosoftVisualStudio/MyProjects/TRACE/TRACEDlg.cpp(181) : {89} normal block at 0x004213B0, 4 bytes long.
Data: CD CD CD CD 
Object dump complete.


注意:0xCD表示已经分配的数据
       0xDD表示已经释放的数据
       0xFD表示被保护的数据

断言
1.断言的特征:
(1)发现运行时刻错误(用户输入错误,资源分配错误,文件系统错误,硬件错误或其他类型错误)
(2)断言中的布尔表达式显示的是某个对象或者状态的有效性,而不是正确性
(3)断言在条件编译后只存在调试版本里面,特别是,断言只在_DEBUG符号定义后,才能编译!
(4)断言不能包含程序代码,也不能有副作用,不能修改程序变量,也比能调用修改程序变量的函数
(5)断言只是给程序员提供有用信息的
2.断言的类型
(1)ANSI C断言
Void assert(expression)     包含在assert.h头文件中(最好不用assert)
原因:*当文件名太长的化,对话框显示的路径将会被截至掉!
*函数是由ANSI NDEBUG函数驱动的,如果定义了NDEBUG后,断言就被取消!

如果要启用JIT调试(Just-in-time),在ToolsOptionsDebug
中选择Just-in-time debugging,默认也会勾选上OLE RPC debugging
单击“重试(R)”就会显示出错误所在的标记行。

(2)C运行时刻函数库断言
_ASSERT(Boolean Expression)   (crtdbg.h)[不用]

_ASSERTE(Boolean Expression)   (crtdbg.h)[经常用这个]

_ASSERTE宏更能给出更多简洁,有用的信息,显示了断言!

 

(3)MFC库中的断言

 

ASSERT(Boolean expression) ASSERT宏和_ASSERT宏显示的消息框相同。VERIFY(Boolean expression) VERIFY 中的BOOL表达式在发布版本中被保留了下来。它简化了对返回值的判断!

 

CString str;

 

VERIFY(str.LoadString(IDS_STRING));//不要用VERIFY宏

 

ASSERT_VALID宏,被用来决定一个指向CObject派生类的对象的指针是否有效。ASSERT(pObjectDerivedFromCObject);主要是在使用CObject派生类对象之前调用,检查对象的有效性。

 

ASSERT_KINDOF(className,pObjectDerivedFromCObject);

 

ASSERT_POINTER(pointer,pointerType);

 

ASSERT_NULL_OR_POINTER(pointer,pointerType);

 

AfxlsValidAddress(const void*memoryAddress,UINT memoryBytes,BOOL isWriteable = TRUE);

 

BOOL AfxlsValidString (LPCSTR string, int stringLength = -1);

 

(4)ATL断言

 

如果你使用ATL,crtdbg.h就包含在atlbase.h中。在任何ATL代码中,ATLASSERT才是你的选择,在atldef.h中你会发现ATLASSERT是_ASSERT的一个别名。

 

优点:在ATL程序中使用ATLASSERT可以让你使用自己的断言。

 

(5)考虑使用_ASSERTE(FALSE)来简化防御性的编程和断言的结合,要想得到描述性的断言消息,考虑使用_ASSERTE(“Problem description.”==0).

 

_ASSERTE("This is the object requires the MM_TEXT mapping mode" == 0);

 

If (!expression)

 

{

 //handle error 

_ASSERT(FALSE); 

… 

}

If(FAILED(SomeFunction())) 

{

//handle error 

_ASSERT(FALSE) 

}

 (6)考虑使用_CrtSetReportMode和_CrtSetReportFile

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn