调试程序的简单说明_第1页
调试程序的简单说明_第2页
调试程序的简单说明_第3页
调试程序的简单说明_第4页
调试程序的简单说明_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、难怪很多前辈说调试是一个程序员最基本的技能,其重要性甚至超过学习一门语言。不会调试的程序员就意味着他即使会一门语言,却不能编制出任何好的软件。我以前接触的程序大多是有比较成形的思路和方法,调试起来出的问题都比较小,最近这个是我自己慢慢摸索调试,接触了很多新的调试方法,并查了很多前辈的总结,受益匪浅,总结以前的和新的收获如下:VC 调试篇设置为了调试一个程序,首先必须使程序中包含调试信息。一般情况下,一个从AppWizard 创建的工程中包含的Debug Configuration 自动包含调试信息,但是是不是Debug 版本并不是程序包含调试信息的决定因素,程序设计者可以在任意的Configu

2、ration 中增加调试信息,包括Release 版本。为了增加调试信息,可以按照下述步骤进行:打开Project settings 对话框(可以通过快捷键ALT+F7打开,也可以通过IDE 菜单Project/Settings 打开 选择C/C+页,Category 中选择general ,则出现一个Debug Info 下拉列表框,可供选择的调试信息 方式包括:命令行 Project settings 说明无 None 没有调试信息/Zd Line Numbers Only 目标文件或者可执行文件中只包含全局和导出符号以及代码行信息,不包含符号调试信息/Z7 C 7.0- Compatib

3、le 目标文件或者可执行文件中包含行号和所有符号调试信息,包括变量名及类型,函数及原型等/Zi Program Database 创建一个程序库(PDB,包括类型信息和符号调试信息。/ZI Program Database for Edit and Continue 除了前面/Zi 的功能外,这个选项允许对代码进行调试过程中的修改和继续执行。这个选项同时使#pragma 设置的优化功能无效选择Link 页,选中复选框"Generate Debug Info",这个选项将使连接器把调试信息写进可执行文件和DLL 如果C/C+页中设置了Program Database 以上的选

4、项,则Linkincrementally 可以选择。选中这个选项,将使程序可以在上一次编译的基础上被编译(即增量编译,而不必每次都从头开始编译。 调试方法:1、使用 Assert(原则:尽量简单assert只在debug下生效,release下不会被编译。2、防御性的编程3、使用Trace4、用GetLastError来检测返回值,通过得到错误代码来分析错误原因5、把错误信息记录到文件中位置断点(Location Breakpoint大家最常用的断点是普通的位置断点,在源程序的某一行按F9就设置了一个位置断点。但对于很多问题,这种朴素的断点作用有限。譬如下面这段代码:void CForDebu

5、gDlg:OnOK(for (int i = 0; i < 1000; i+ /Aint k = i * 10 - 2; /BSendTo(k; /Cint tmp = DoSome(i; /Dint j = i / tmp; /E执行此函数,程序崩溃于E行,发现此时tmp为0,假设tmp本不应该为0,怎么这个时候为0呢?所以最好能够跟踪此次循环时DoSome函数是如何运行的,但由于是在循环体内,如果在E行设置断点,可能需要按F5(GO许多次。这样手要不停的按,很痛苦。使用VC6断点修饰条件就可以轻易解决此问题。步骤如下。1 Ctrl+B打开断点设置框,如下图: Figure 1设置高级

6、位置断点2 然后选择D行所在的断点,然后点击condition按钮,在弹出对话框的最下面一个编辑框中输入一个很大数目,具体视应用而定,这里1000就够了。3 按F5重新运行程序,程序中断。Ctrl+B打开断点框,发现此断点后跟随一串说明:.487 times remaining。意思是还剩下487次没有执行,那就是说执行到513(1000-487次时候出错的。因此,我们按步骤2所讲,更改此断点的skip次数,将1000改为513。4 再次重新运行程序,程序执行了513次循环,然后自动停在断点处。这时,我们就可以仔细查看DoSome是如何返回0的。这样,你就避免了手指的痛苦,节省了时间。再看位置

7、断点其他修饰条件。如Figure 1所示,在“Enter the expression to be evaluated:”下面,可以输入一些条件,当这些条件满足时,断点才启动。譬如,刚才的程序,我们需要i为100时程序停下来,我们就可以输入在编辑框中输入“i=100”。另外,如果在此编辑框中如果只输入变量名称,则变量发生改变时,断点才会启动。这对检测一个变量何时被修改很方便,特别对一些大程序。用好位置断点的修饰条件,可以大大方便解决某些问题。数据断点(Data Breakpoint软件调试过程中,有时会发现一些数据会莫名其妙的被修改掉(如一些数组的越界写导致覆盖了另外的变量,找出何处代码导致这

8、块内存被更改是一件棘手的事情(如果没有调试器的帮助。恰当运用数据断点可以快速帮你定位何时何处这个数据被修改(最好使用内存来查找,使用变量名的话,IDE不一定能找到。譬如下面一段程序:#include "stdafx.h"#includeint main(int argc, char* argvchar szName110;char szName24;strcpy(szName1,"shenzhen"printf("%sn", szName1; /Astrcpy(szName2,"vckbase" /Bprintf(

9、"%sn", szName1;printf("%sn", szName2;return 0;这段程序的输出是szName1: shenzhenszName1: aseszName2: vckbaseszName1何时被修改呢?因为没有明显的修改szName1代码。我们可以首先在A行设置普通断点,F5运行程序,程序停在A行。然后我们再设置一个数据断点。如下图: Figure 2数据断点F5继续运行,程序停在B行,说明B处代码修改了szName1。B处明明没有修改szName1呀?但调试器指明是这一行,一般不会错,所以还是静下心来看看程序,哦,你发现了:sz

10、Name2只有4个字节,而strcpy了7个字节,所以覆写了szName1。数据断点不只是对变量改变有效,还可以设置变量是否等于某个值。譬如,你可以将Figure 2中红圈处改为条件”szName20=''''y''''“,那么当szName2第一个字符为y时断点就会启动。可以看出,数据断点相对位置断点一个很大的区别是不用明确指明在哪一行代码设置断点。上图中的断点设置最好用内存地址来表示,否则vc会出错。其他调试手段:系统提供一系列特殊的函数或者宏来处理Debug版本相关的信息,如下:宏名/函数名说明TRACE 使用方法和prin

11、tf完全一致,他在output框中输出调试信息ASSERT 它接收一个表达式,如果这个表达式为TRUE,则无动作,否则中断当前程序执行。对于系统中出现这个宏导致的中断,应该认为你的函数调用未能满足系统的调用此函数的前提条件。例如,对于一个还没有创建的窗口调用SetWindowTextVERIFY 等。 和 ASSERT 功能类似, 所不同的是, Release 版本中, 在 ASSERT 不计算输入的表达式的值,而 VERIFY 计算表达式的值。 使用_ASSETE 来 debug,这三个都是 MFC 的。_ASSERTE 的头文件是 crtdbg.h。 值 Watch VC 支持查看变量、表

12、达式和内存的值。所有这些观察都必须是在断点中断的情 况下进行。 观看变量的值最简单,当断点到达时,把光标移动到这个变量上,停留一会 就可以看到变量的值。 VC 提供一种被成为 Watch 的机制来观看变量和表达式的值。在断点状态下, 在变量上单击右键,选择 Quick Watch, 就弹出一个对话框,显示这个变量的 值。 单击 Debug 工具条上的 Watch 按钮,就出现一个 Watch 视图 (Watch1,Watch2,Watch3,Watch4),在该视图中输入变量或者表达式,就可以 观察 变量或者表达式的值。注意:这个表达式不能有副作用,例如+运算符绝 对禁止用于这个表达式中,因为

13、这个运算符将修改变量的值,导致 软件的逻辑 被破坏。 也可以修改某个变量的值。 Memory 由于指针指向的数组,Watch 只能显示第一个元素的值。为了显示数组的后续内 容,或者要显示一片内存的内容,可以使用 memory 功能。在 Debug 工具条上点 memory 按钮,就弹出一个对话框,在其中输入地址,就可以显示该地址指向的 内存的内容。 Variables Debug 工具条上的 Variables 按钮弹出一个框,显示所有当前执行上下文中可见 的变量的值。特别是当前指令涉及的变量,以红色显示。 寄存器 Debug 工具条上的 Registers 按钮弹出一个框,显示当前的所有寄存

14、器的值。 调试技巧: 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、查

15、看映射文件:见” 通过崩溃地址找出源代码的出错行”文件。 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、调试内存变量的变化:当内存发生变化时停下来。? 进程控制

16、 VC 允许被中断的程序继续运行、单步运行和运行到指定光标处,分别对应快捷 键 F5、F10/F11 和 CTRL+F10。各个快捷键功能如下: 快捷键 F5 F10 F11 CTRL+F10 F7 F9 Ctrl+Shift+F9 Shift+F5 说明 调试/继续运行 单步,如果涉及到子函数,不进入子函数内部 单步,如果涉及到子函数,进入子函数内部 运行到当前光标处。 重建 设置断点/清除断点 清除所有断点 结束调试 Call Stack 调用堆栈反映了当前断点处函数是被那些函数按照什么顺序调用的。单击 Debug 工具条上的 Call stack 就显示 Call Stack 对话框。在

17、 CallStack 对话框中显示 了一个调用系列,最上面的是当前函数,往下依次是调用函数的上级函数。单击 这些函数名可以跳到对应的函数中去。 关注 一个好的程序员不应该把所有的判断交给编译器和调试器, 应该在程序中自己加 以程序保护和错误定位,具体措施包括: · · · · 对于所有有返回值的函数,都应该检查返回值,除非你确信这个函数调用 绝对不会出错,或者不关心它是否出错。 一些函数返回错误,需要用其他函数获得错误的具体信息。例如 accept 返回 INVALID_SOCKET 表示 accept 失败,为了查明 具体的失败原因,应 该立刻用 WS

18、AGetLastError 获得错误码,并针对性的解决问题。 有些函数通过异常机制抛出错误,应该用 TRY-CATCH 语句来检查错误 程序员对于能处理的错误,应该自己在底层处理,对于不能处理的,应该 报告给用户让他们决定怎么处理。如果程序出了异常, 却不对返回值和 其他机制返回的错误信息进行判断,只能是加大了找错误的难度。 另外:VC 中要编制程序不应该一开始就写 cpp/h 文件,而应该首先创建一个合 适的工程。因为只有这样,VC 才能选择合适的编译、连接 选项。对于加入到工 程中的 cpp 文件,应该检查是否在第一行显式的包含 stdafx.h 头文件,这是 Microsoft Visual Studio 为了加快编译 速度而设置的预编译头文件。在这个 #include "stdafx.h"行前面的所有代码将被忽略,所以其他头文件应该在这一 行后面

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论