设置 打开Project settings对话框(可以通过快捷键ALT+F7打开,也可以通过IDE菜单Project/Settings打开) 选择Link页,选中复选框"Generate Debug Info",这个选项将使连接器把调试信息写进可执行文件和DLL 设置断点:可以通过下述方法设置一个断点。首先把光标移动到需要设置断点的代码行上,然后 按F9快捷键 条件断点:可以为断点设置一个条件,这样的断点称为条件断点。对于新加的断点,可以单击Conditions按钮,为断点设置一个表达式。当这个表达式发生改变时,程序就 被中断。底下设置包括“观察数组或者结构的元素个数”,似乎可以设置一个指针所指向的内存区的大小,但是我设置一个比较的值但是改动 范围之外的内存区似乎也导致断点起效。{zh1}一个设置可以让程序先执行多少次然后才到达断点。 数据断点:数据断点只能在Breakpoints对话框中设置。选择“Data”页,就显示了设置数据断点的对话框。在编辑框中输入一个表达式,当这个 表达式的值发生变化时,数据断点就到达。一般情况下,这个表达式应该由运算符和全局变量构成,例如:在编辑框中输入 g_bFlag这个全局变量的名字,那么当程序中有g_bFlag= !g_bFlag时,程序就将停在这个语句处。 消息断点:VC也支持对Windows消息进行截获。他有两种方式进行截获:窗口消息处理函数和特定消息中断。 值 Memory Varibles 寄存器 进程控制 快捷键 说明 Call Stack 其他调试手段 宏名/函数名 说明 关注 对于所有有返回值的函数,都应该检查返回值,除非你确信这个函数调用{jd1}不会出错,或者不关心它是否出错。 弹出Project settings对话框 ------------------------------------------------------------------------------------ 便于调试的代码风格:不用全局变量 所有变量都要初始化,成员变量在构造函数中初始化 尽量使用const 详尽的注释 VC++编译选项:总是使用/W4警告级别 在调试版本里总是使用/GZ编译选项,用来发现在Release版本中才有的错误 没有警告的编译:保证在编译后没有任何警告,但是在xx警告前要进行仔细检查 调试方法: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;} b)、ATLTRACEc)、afxDump 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,输出程序代码地址: /MAPINF LINES,得到引出序号:/MAPINF 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、MFCa)、使用错误的函数原型处理用户定义消息正确的函数原型为: 3、谨慎使用TerminateThread:使用TerminateThread会造成资源泄漏,不到万不得已,不要使用。4、使用_beginthreadex,不要使用Create Thread来常见线程。参考资料:《Windows程序调试》 -------------------------------------------------------------------------------------- 其实VC所依赖的开发平台Microsoft Developer Studio本身提供的调试功能并不弱,每当我们创建一个新的VC工程项目时,默认状态就是Debug(调试)版本,在"组建"(Build)菜单下的Configurations中可以看到除了调试版本还可以设置成发行(Release)版本。调试版本由于包含了大量信息,所以它生成的可执行程序容量会远远大于发行版。 在使用微软程序开发库MSDN时,我们会发现其中的VC示例经常采用看似多余的ASSERT语句,其作用就是使程序具有"维护"性。对于Debug版本的VC程序,在遇到布尔值为FALSE的ASSERT语句处停止,并显示Assertion Failed对话;如果设置为发布版,所有ASSERT语句都会被预处理程序删除。一个地道的VC编程员,应该有意在自己的代码中通过"维护"特征去检测任何设定,诸如输入参数、循环范围和变量值的设定。
代码运行的结果为: 在MFC类和VC中本身就有"异常情况"这个概念,并在此基础上形成它们处理系统错误和意外的主要机制。比如当系统内存分配殆尽时,你的运行程序就会收到内存异常的消息。这样就给了程序员xx异常的机会。 MFC中的异常情况主要有:CArchiveException表示档案文件载入或保存时出错,CDBException属于数据库错误,CFileException为文件错误,CMemoryException为调用new时发生分配错误,CNotSupportedException表示指定操作不被支持,COleException表示在调用OLE操作时出错,COleDispatchException表示在OLE自动操作时出错,CResourceException表示资源找不到或无法创建,CUserException用于通知用户错误。 MFC还包含一系列以Afx-为词头的调试函数:AfxAbort可以在发生致命错误时异常终止程序,AfxCheckMemory可以检查堆和剩余缓冲池的受损部分;AfxDoForAllClasses重声明所有CObject的派生类;AfxDoForAllObject重声明堆上所有CObject派生的对象;AfxEnableMemoryTracking启用或禁止内存追踪;AfxIsMemoryBlock用于确认指针所指内存有效;AfxIsValidAddress用于确认地址是驻留在程序的内存区域内;AfxIsValidString用于确认地址所指字符串有效;AfxSetAllocHook用于内存分配前进行检测;AfxTraceEnabled启动或禁止输出跟踪,AfxTraceFlags则进一步定制跟踪特征。 在我们随手编制的VC程序中,普遍存在着会发生内存泄漏的隐患,有些问题程序的痼疾症状是在处理数据量激增时陷入瘫痪,更糟的要发现内存泄漏并不容易。首先,我们要明确VC中内存泄漏的含义:简单说就是一个程序申请得到了一段内存却没有及时释放。比如用new在堆中分配了一个对象或对象组却并没有调用delete操作。灵活的指针技术使内存泄漏的原因变得复杂化,比如改变了保存在一变量中的指针的值后未能删除指针所指向的内存区;当内存泄漏是来自一个带有指针类型成员变量的类时会更加困难,因为当调用分配指针时并没有复制构造函数/析构函数或运算符。 为了防止发生内存泄漏这样棘手的故障,在VC编程时应当注意遵循几个规范:其一,如果一个类包含有指针并且分配了指针值,那么就需要构造相应的析构函数以删除该指针;其二,如果一个函数分配了一块内存并把该内存块返回给调用它的函数使用,那么它返回的必须是一个指针而非一个引用,因为引用不能被程序删除;其三,即使一个函数分配了一段内存并在同一函数的稍后部分删除了该内存段,也要尽可能将内存块分配到堆栈中;{zh1},就是决不要试图改变一个指针值,除非已经删除指针所指的对象或通过数组指向了该指针所指向的内存,而且也不要对new返回的指针进行加1运算。 每当编写VC程序时,我们都会处于一个琳琅满目的集成开发环境(IDE)中,现实的真相是我们很多人在这里编程多年,对开发环境了解并不全面和细微。记得王朔的小说中有句话说"穿了多年的外套在不穿时才发现它原来还有一个兜!"。我们在安装VC时,得到的IDE即Developer Studio, VC其实是Developer Studio下xx的一个组件而已,比如微软的VJ++也是基于Developer Studio。很少有技术书籍会一一介绍Developer Studio界面元素,也许聪明的程序员轻易就能识别其含义,全部猜对界面图符的含义并非易事。可是它们对我们了解开发信息很重要,也与调试程序有关联。 在Developer Studio下会生成多个文件去保存项目的所有信息:一个是以.DSW为扩展名的项目工作区文件,它包含项目中所有文件的名称、文件所在目录、编译器和连接器的选项以及项目工作的其它信息;以.DSP为扩展名的也是项目记录文件,.OPT是工作区选项文件,它包含Developer Studio的所有个人设置 - 包括颜色、字体、工具栏、哪个文件被打开以及MDI窗口如何被定位和{zx1}调试中的断点等。在打开项目工作区文件时其它文件随即会自动打开。在Developer Studio下可以按类查看代码,其中的ClassView显示了应用程序中所有的类,每个类下显示了成员函数和数据成员,在成员函数旁有粉红图标,数据成员旁是蓝绿色图标,保护类成员的图标旁有一枚钥匙,私有类成员则有一个挂锁图标。 当然,在开发环境下最主要的工作是输入编辑程序源代码,源代码会显示"语法着色"。在缺省情况下,代码为黑色,夹以绿色的注释和蓝色的关键字(指VC所保留的public、private、new和int等等)。--这些地球人都知道,但是为了调试需要,我们还可以指定颜色去显示字符串、数字和运算符。定义方法是通过Tools菜单下的Options对话框中的Format选项卡设置。 在Developer Studio提供的诸多菜单项中,我们往往对少数菜单避而不用,因为不了解它们的作用唯恐好奇心会造成乱子;还有多个菜单项都可以达到目的,至于它们之间的微小差别则不甚了了。比如在编译和调试时常用到Build菜单组,它具有和应用程序编译、运行调试相关的多项操作:其中的Compile菜单会编译当前的聚焦文件;Build菜单会编译和链接所有在项目中修改的文件;Build All会编译链接项目中所有文件,包括最近编译后没有修改过的文件;Batch Build用于包含有Debug和Release配置的项目;Clean会删除所有的中间和输出文件,因而项目目录下仅包含源文件;Debugger Remote Connection用于远程调试,即在一台机器运行程序而在另一台调试;Set Active Configuration可设置某个配置为xx状态(Debug或Release);Profiler能够识别应用程序的瓶颈,即找到降低程序执行速度的代码和有关模块,为此需要在"工程/设置/Link"下勾选Profiling。 VC中的警告信息是有级别的。在"工程/设置"下的"C/C++"选项卡中的警告级别Warning Level缺省值是3,如果改为更为严格的4级,往往会产生更多的警告类错误。在C/C++选项卡中还提供了代码优化栏Optimizations,在你完成漫漫调试之旅准备正式发布前夕,你应当改动此项,它提供了一些适合建立发行版应用程序的优化设置。 其实,在多年以前,在软件业中针对开发具有一定规模软件项目的情况就出现了软件工程理论,以此来指导软件人员树立团队协作意识,进而保证软件项目的协调性及进度质量等。笔者在从事VC开发中对此很有感触,而且觉得对于每个开发人员自己也应当具备一定的工程素养。比如,我们在开发初期时,应当对所编写的有价值的源代码及时备份,即使有些代码在后续阶段似乎没有用处了也不妨“敝帚自珍”。在VC编程中我经常遇到的问题是,一个不算小的编写了很久的程序,在稍微扩展一点功能时出现了故障,而最挠头的时花了很多时间也无法排故,所幸的是对原来的程序我还有备份,可以重新再来--功能还要加,只是还一种编程思路去实现相同的功能。说实在的,本人在VC开发中,如果要总结“解决了多少问题”,不如说很多时候是采用"游击战"巧妙地"绕过"了一些棘手的问题。--尤其在软件交工处于xxx的开发后期出现的故障,往往你已经没有时间去找到故障,而是用前一个完好的VC Project去尝试另一条捷径。 |