zoukankan      html  css  js  c++  java
  • 转 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 << time << “/n”;
    #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的指针和引用支持插入操作符(<<)。有几个CObject派生的类也有定 义的插入操作符,CPoint,CSize,CRect,CString,CTime和CTimeSpan。
    #if _DEBUG
    AfxDump(pObiect);
    pObject->Dump(afxDump);
    afxDump<<pObject;
    #endif

    例1-3:
    afxDump<<_T(“Warning:This object doesn’t seem right:/n”)<<pObject;
    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&#61664;Link&#61664;Category&#61664;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<<longString;//dosen’t assert for long strings 不需要判断
    #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&#61664;Settings&#61664;Link&#61664;Generate mapfile.即可在工程的Debug文件下看到。映射文件里面所有的公共符号都使用混合名字。可使用VC++的名字解析工具(Undname)将混合 名字转换到原始名字。你可以在“开始”&#61664;”运行”&#61664;”cmd”&#61664;输入
    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&#61664;Options&#61664;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

    http://blog.csdn.net/yaneng/article/details/5660889

  • 相关阅读:
    Django项目总结:Model高级
    Django项目总结:Response和Cookie
    数据库:索引
    数据库:视图
    数据库:约束
    MySQL:常用命令
    MySQL:安装和连接
    时间戳和正则表达式
    Java 类的概念与使用
    JAVA复习题
  • 原文地址:https://www.cnblogs.com/aprilapril/p/3180560.html
Copyright © 2011-2022 走看看