收藏 分享(赏)

vc的调试技术.doc

上传人:yjrm16270 文档编号:6732554 上传时间:2019-04-21 格式:DOC 页数:10 大小:47KB
下载 相关 举报
vc的调试技术.doc_第1页
第1页 / 共10页
vc的调试技术.doc_第2页
第2页 / 共10页
vc的调试技术.doc_第3页
第3页 / 共10页
vc的调试技术.doc_第4页
第4页 / 共10页
vc的调试技术.doc_第5页
第5页 / 共10页
点击查看更多>>
资源描述

1、VC程序调试技术VC程序调试技术VC 程序调试技术 在开发程序的过程中,经常需要查找程序中的错误,这就需要利用调试工具来帮助你进行程序的调试,当然目前有许多调试工具,而集成在 VC 中的调试工具以其强大的功能,一定使你爱不释手。下面我们先来介绍 VC 中的调试工具的使用。1 VC 调试工具11 调试环境的建立在 VC 中每当建立一个工程(Project)时,VC 都会自动建立两个版本:Release 版本,和 Debug 版本,正如其字面意思所说的,Release 版本是当程序完成后,准备发行时用来编译的版本,而 Debug 版本是用在开发过程中进行调试时所用的版本。DEBUG 版本当中,包含

2、着 MICROSOFT 格式的调试信息,不进行任何代码优化,而在 RELEASE 版本对可执行程序的二进制代码进行了优化,但是其中不包含任何的调试信息。在新建立的工程中,你所看到是 DEBUG 版本,若要选择 RELEASE 版本,可以选择菜单 PROJECT 中的 SETTING 命令,这时屏幕上面弹出 PROJECT SETTEING 对话框,在 SETTING FOR 下拉列表中选择 RELEASE,按 OK 退出,如图 4.1。图 4.1在调试程序的时候必须使用 DEBUG 版本,我们可以在 Project Setting 对话框的 C/C+页中设置调试选项。图 4.2各个选项的含意如

3、下: Program Database 表示产生一个存储程序信息的数据文件(.PDB),它包含了类型信息和符号化的调试信息; Line Numbers Only 表示程序经过编译和链接产生的.OBJ 或.EXE 文件仅仅包含全局和外部符号以及行号信息; C7 Compatible 表示产生一个.OBJ 或.EXE 文件行号信息以及符号化的调试信息; None 表示不产生任何调试信息。12 调试的一般过程调试,说到底就是在程序的运行过程的某一阶段观测程序的状态,而在一般情况下程序是连续运行的,所以我们必须使程序在某一地点停下来。所以我们所做的第一项工作就是设立断点。其次,再运行程序,当程序在设立

4、断点处停下来时,再利用各种工具观察程序的状态。程序在断点停下来后,有时我们需要按我们的要求控制程序的运行,以进一步观测程序的流向,所以下面我们依次来介绍断点的设置,如何控制程序的运行以及各种观察工具的利用。13 如何设置断点在 VC 中,你可以设置多种类型的断点,我们可以根据断点起作用的方式把这些断点分为三类:1 、与位置有关的断点;2、与逻辑条件有关的断点 3、与 WINDOWS 消息有关的断点下面我们分别介绍这三类断点。首先我们介绍与位置有关的断点。1、 最简单的是设置一般位置断点,你只要把光标移到你要设断点的位置,当然这一行必须包含一条有效语句的;然后按工具条上的 add/remove

5、breakpoint 按钮或按快捷键 F9;这时你将会在屏幕上看到在这一行的左边出现一个红色的圆点表示这二设立了一个断点。图 4.32 、有的时候你可能并不需要程序每次运行到这儿都停下来,而是在满足一定条件的情况下才停下来,这时你就需要设置一种与位置有关的逻辑断点。要设置这种断点我们只需要从 EDIT 菜单中选中 breakpoint命令,这时 Breakpoint 对话框将会出现在屏幕上。选中 Breakpoint 对话框中的 LOCATION 标签,使LOCATION 页面弹出,如图 4.4图 4.4单击 condition 按钮,弹出 Breakpoint 对话框,在 Expressio

6、n 编辑框中写出你的逻辑表达式,如 X=3或 a+b25,最后按 OK 返回。图 4.5这种断点主要是由其位置发生作用的,但也结合了逻辑条件,使之更灵活。3、有时我们需要更深入地调试程序,我们需要进入程序的汇编代码,因此我们需要在在汇编代码上设立断点:要设立这种断点我们只需从 View 菜单中选 Debug window 命令 ,图 4.6再选 Disassembly 子命令,这时汇编窗口将会出现在屏幕上。图 4.7在图 4.7 中的汇编窗口中你将看到对应于源程序的汇编代码,其中源程序是用黑体字显示,下面是且对应的汇编代码。要设立断点,我们只需将光标移到你想设断点处然后点击工具条上的 Inse

7、rt/Remove Breakpoints 按钮,此后你将会看到一个红圆点出现在该汇编代码的右边。图 4.8上面所讲的断点主要是由于其位置发挥作用的,即当程序运行到设立断点的地方时程序将会停下来。但有时我们设立只与逻辑条件有关的断点,而与位置无关。所以下面介绍一下与逻辑条件有关的断点。(1)逻辑条件触发断点的设置:l 从 EDIT 菜单中选中 breakpoint 命令,这时屏幕上将会出现 Breakpoint 对话框。图 4.9l 选中 Breakpoint 对话框中的 DATA 标签,对应的页面将会弹出图 4.10l 在图 4.10 的 DATA 页面中的 Expression 编辑框中写

8、出你的逻辑表达式,如 (X=3);图 4.11最后按 OK 返回。其他几种断点的设置的方法都与之类似。我们一一加以说明。(2)监视表达式发生变化断点:从 EDIT 菜单中选中 breakpoint 命令,这时屏幕上将会出现 Breakpoint 对话框。选中 Breakpoint 对话框中的 DATA 标签,对应的页面将会弹出。 在 Expression 编辑框中写出你需要监视的表达式。最后按OK 键返回。(3)监视数组发生变化的断点:l 从 EDIT 菜单中选中 breakpoint 命令,这时屏幕上将会 出现 Breakpoint 对话框。l 选中 Breakpoint 对话框中的 DAT

9、A 标签,对应的页面将会弹出l 在 Expression 编辑框中写出你需要监视数组名;l 在 Number of Elements 编辑框输入你需要监视数组元素的个数;l 按 OK 键返回。(4)监视由指针指向的数组发生变化的断点:l 从 EDIT 菜单中选中 breakpoint 命令,这时在屏幕上将会出现 Breakpoint 对话框。l 选中 Breakpoint 对话框中的 DATA 标签;l 在 Expression 编辑框中输入形如*pointname,其中*pointname 为指针变量名;l 在 Number of Elements 编辑框输入你需要监视数组元素的个数;l 按

10、 OK 键返回。(5)监视外部变量发生变化的断点:l 从 EDIT 菜单中选中 breakpoint 命令这时屏幕上将会出现 Breakpoint 对话框;l 选中 Breakpoint 对话框中的 DATA 标签;l 在 Expression 编辑框中输入变量名;l 点击在 Expression 编辑框的右边的下拉键头;l 选取 Advanced 选项,这时 Advanced Breakpoint 对话框出现;l 在 context 框中输入对应的函数名和( 如果需要的话)文件名;l 按 OK 键关闭 Advanced Breakpoint 对话框。l 按 OK 键关闭 Breakpoint

11、s 对话框。(6)在讲了位置断点和逻辑断点之后我们再讲一下与 WINDOWS 消息有关的断点。注意:此类断点只能工作在 x86 或 Pentium 系统上。l 从 EDIT 菜单中选中 breakpoint 命令,这时屏幕上将会出现 Breakpoint 对话框;l 选中 Breakpoint 对话框中的 MESSAGE 标签,对应的页面将会弹出;l 在 Break At WndProc 编辑框中输入 Windows 函数的名称;l 在 Set One Breakpoint From Each Message To Watch 下拉列表框中选择对应的消息;l 按 OK 返回。14 控制程序的运

12、行上面我们讲了如何设置各类断点,下面我们来介绍如何控制程序的运行。当我们从菜单 Build 到子菜单Start Debuging 选择 Go 程序开始运行在 Debug 状态下,程序会由于断点而停顿下来后,可以看到有一个小箭头,它指向即将执行的代码。图 4.12随后,我们就可以按要求来控制程序的运行:其中有四条命令:Step over, step Into , Step Out ,Run to Cursor。图 4.13在图 4.13 中:Step over 的功能是运行当前箭头指向的代码(只运行一条代码) 。Step Into 的功能是如果当前箭头所指的代码是一个函数的调用,则用 Step

13、Into 进入该函数进行单步执行。Step Out 的功能是如当前箭头所指向的代码是在某一函数内,用它使程序运行至函数返回处。Run to Cursor 的功能是使程序运行至光标所指的代码处。15 查看工具的使用调试过程中最重要的是要观察程序在运行过程中的状态,这样我们才能找出程序的错误之处。这里所说的状态包括各变量的值,寄存中的值,内存中的值,堆栈中的值 ,为此我们需要利用各种工具来帮助我们察看程序的状态。 弹出式调试信息泡泡(Data Tips Pop_up Information)。当程序在断点停下来后,要观察一个变量或表达式的值的最容易的方法是利用调试信息泡泡。要看一个变量的值,只需在

14、源程序窗口中,将鼠标放到该变量上,你将会看到一个信息泡泡弹出,其中显示出该变量的值。图 4.14要查看一个表达式的值,先选中该表达式,仍后将鼠标放到选中的表达式上,同样会看到一个信息泡泡弹出以显示该表达式的值如图 4.15 所示。图 4.15 变量窗口(VARIABLE WINDOW)。在 VIEW 菜单,Debug window 选 Variables window; 变量窗口将出现在屏幕上。其中显示着变量名及其对应的值。你将会看到在变量观察窗口的下部有三个标签:AUTO ,LOCAL,THIS 选中不同的标签,不同类型的变量将会显示在该窗口中。图 4.16 观察窗口(WATCH WINDO

15、W):在 VIEW 菜单,选择 Debug window 命令,Watch window 子命令。这时变量窗口将出现在屏幕上。图 4.17在图 4.17 的观察窗口中双击 Name 栏的某一空行,输入你要查看的变量名或表达式。图 4.18回车后你将会看到对应的值。观察窗口可有多页,分别对应于标签 Watch1,Watch2,Watch3 等等。假如你输入的表达式是一个结构或是一个对象,你可以用鼠标点取表达式右边的形如 + ,以进一步观察其中的成员变量的值如图4.19。图 4.19 快速查看变量对话框(quick watch);在快速查看变量对话框中你可以象利用观察窗口一样来查看变量或表达式的值

16、。但我们还可以利用它来该变运行过程中的变量,具体操作如下:(1) 在 Debug 菜单,选择 Quick Watch 命令,这时屏幕上将会出现 Quick Watch 对话框;图 4.20(2) 在 Expression 编辑框中输入变量名,按回车;图 4.21(3)在 Current Value 格子中将出现变量名及其当前对应的值如图 4.22: 图 4.22(4 )如要改变该变量的值只需双击该变量对应的 Name 栏,输入你要改变的值;(5 )如要把该变量加入到观察窗口中,点击 Add watch 按钮;(6 )点击 Close 按钮返回; 我们还可以直接查看内存中的值(1)从 View

17、菜单中选取 Debug windows 及 Memory 子命令。Memory Window 出现;图 4.23(2)在 Address 编辑框中输入你要查看的内存地址,回车。对应内存地址中的值将显示在 Memory window 的窗口中。图 4.24 在调试过程中,有时我们需要查看或改寄存器中的值。我们只需:(1 )从 View 菜单中选取 Debug window 及 Registers 子选项。Registers 窗口出现。在 Registers 窗口中,信息以 Register = Value 的形式显示,其中 Register 代表寄存器的名字,Value 代表寄存器中的值。图 4

18、.25(2 )如果你要修改某一个寄存器的值,用 TAB 键,或鼠标将光标移到你想改变的值的右边,然后输入你想要的值。回车返回。在寄存器中,有一类特殊的寄存器称为标志寄存器,其中有八个标志位:OV 是溢出标志;UP 是方向标志;EI 是中断使能标志;Sign 是符号标志,Zero 是零标志。Parity 是奇偶较验标志。Carry 是进位标志。2 高级调试技术前面我们讲了调试工具的使用,利用它可以就进行常规的调试,即使程序在某处停下来,再观察程序的当前壮态。而且这些工具在且它调试器中也有。但我们知道我们知道在 VC 程序的开发过程中,光有这些工具是不够的。为了更快更好地开发程序,我们还需要利用更

19、高级的调试工具。我们知道,在利用 VC 开发过程中,利用 MFC 将会极大地方便应用程序的开发,所以开发人员往往是利用 MFC 来开发应用程序,正是这个原因 Microsoft 公司在 MFC 中提供了一些特性来帮助你进行程序的调试。我们知道在 MFC 中,绝大多数类都是从一个叫做 Cobject 的类继承过来的,虽然这是一个虚基类,但它定义了许多成员函数,其中许多成员函数是用来支持程序的调试的,如 Dump ,Assertvalid 等成员函数。另外他们都支持如 TRACE,ASSERT 等宏,并支持内存漏洞的检查等等。我们知道,为了支持调试,类库肯定在在性能上有所损失,为此 Microso

20、ft 公司提供了两个不同的版本的类库:Win32 Debug 版本和Win32 Release 版本。在前面我们已经提到,每当我们建立一个工程时,我们也有对应的两个版本。在你的 DEBUG 版本的工程中,编译器连接 DEBUG 版本的 MFC 类库;在你的 RELEASE 版本的工程中编译器连接 RELEASE 版本的 MFC 类库以获得尽可能快的速度。下面我们来介绍这些工具的利用。2.1 TRACE 宏的利用TRACE 宏有点象我们以前在 C 语言中用的 Printf 函数,使程序在运行过程中输出一些调试信息,使我们能了解程序的一些状态。但有一点不同的是:TRACE 宏只有在调试状态下才有所

21、输出,而以前用的Printf 函数在任何情况下都有输出。和 Printf 函数一样,TRACE 函数可以接受多个参数如:int x = 1;int y = 16;float z = 32.0;TRACE( “This is a TRACE statementn“ );TRACE( “The value of x is %dn“, x );TRACE( “x = %d and y = %dn“, x, y );TRACE( “x = %d and y = %x and z = %fn“, x, y, z );要注意的是 TRACE 宏只对 Debug 版本的工程产生作用,在 Release 版本

22、的工程中,TRACE 宏将被忽略。2.2 ASSERT 宏的利用在开发过程中我们可以假设只要程序运行正确,某一条件肯定成立。如不成立 ,那么我们可以断言程序肯定出错。在这种情况下我们可以利用 ASSERT 来设定断言。ASSERT 宏的参数是一个逻辑表达式,在程序运行过程中,若该逻辑表达式为真,则不会发生任何动作,若此表达式为假,系统将弹出一个对话框警告你,并停止程序的执行。同时要求你作出选择:Abort,Ignore,Retry。若你选择 Abort,系统将停止程序的执行;若你选择 Ignore 系统将忽略该错误,并继续执行程序;若你选择 Retry ,系统将重新计算该表达式,并激活调试器。

23、同 TRACE 宏一样,ASSERT 宏只 DEBUG 版本中起作用,在 RELEASE 版本中 ASSERT 宏将被忽略。2.3 ASSERT_VALID 宏的利用以及类的 AssertValid()成员函的重载ASSERT_VALID 宏用来在运行时检查一个对象的内部合法性,比如说现在 有一个学生对象,我们知道每个学生的年龄一定大于零,若年龄小于零,则该学生对象肯定有问题。事实上,ASSERT_VALID 宏就是转化为对象的成员函数 AssertValid()的调用,只是这种方法更安全。它的参数是一个对象指针,通过这个指针来调用它的 AssertValid()成员函数。与此相配套,每当我们

24、创建从 Cobject 类继承而来的一个新的类时,我们可以重载该成员函数,以执行特定的合法性检查。2.4 对象的 DUMP 函数的利用Dump 函数用来按指定的格式输出一个对象的成员变量,来帮助你诊断一个对象的内部情况。与AssertValid 成员函数一样,Dump 也是 Cobject 类的成员函数。Dump 函数的参数是一个CdumpContext 对象,你可以象利用流一样往向这个对象中输入数据。当你创建一个 Cobject 继承而来的 新类时,你可以按如下步骤重载你自己的 Dump 函数:(1) 调用基类的 Dump 函数,以输出基类的内容;(2) 向 Cdumpcontest 对象输

25、出该类的数据.例如,典型的 Dump 函数定义如下:#ifdef _DEBUGvoid CPerson:Dump( CDumpContext/ now do the stuff for our specific classdc “last name: “ m_lastName “n“ “first name: “ m_firstName “n“;#endif你可能已经注意到整个函数的定义都包含在#ifdef _DEBUG 和#endif 中,这使得 Dump 成员函数只在DEBUG 版本中发生作用,而对 RELEASE 版本不发生作用。3 内存漏洞的检查也许你已经知道,在 C+和 C 语言中指

26、针问题也就是内存申请与释放是一个令人头疼的事情,假如你申请了内存,但没有释放,并且你的程序需要长时间地运行,那么,系统的资源将逐渐减少,当系统的资源全部被用完时,系统将会崩溃。所以在开发程序的过程中一定要保证资源的完全释放。下面我们来介绍内存漏洞的检查。也许你会问,系统是怎样支持内存漏洞的检查的?其实在你的 Debug 版本中所有的有关内存分配的函数都是被重载过的,具体过程是这样的,当你的程序申请内存时,它首先调用一般的内存分配函数分配一块稍大的内存块。在这一内存块中分为四个小块:Heap Information, buffer , User memory block, buffer。第一块为

27、有关堆的信息,比如,申请该内存的地点(文件名,行号),此内存块的类型( 如整型,浮点,或某一类的对象)等等。第二块是一个缓冲区 ,用于截获用户对其申请内存使用越界的情况。第三块是真正给用户的内存,返回的指针也是指向这儿。第四块也是一个缓冲区,作用同第二块。当你申请的内存均被记录在案后,要检查内存漏洞就比较容易了,粗略地说,假如你要检查某一程序段是否有内存漏洞,你只需在这一程序段的开始要求系统为你做一个内存使用情况的映象,记录下程序开始时的内存使用情况,然后在程序段的末尾再使系统为你做一次内存映象,比较两次映象,以检查是否有没释放的内存,假如有未释放的内存,根据这一块中有关分配情况的信息来告诉用

28、户在那儿申请的内存没释放。具体地讲检查内存漏洞需要以下几个步骤:l 在你所检测的程序段的开始处建立一个 CmemoryState 对象,调用其成员函数 Checkpoint,以取得当前内存使用情况的快照;l 在你所检测的程序段的末尾处再建立一个 CmemoryState 对象,调用其成员函数 Checkpoint ,以取得当前内存使用情况的快照;l 再建立第三个 CmemoryState 对象,调用其成员函数 Difference,把第一个 CmemoryState 对象和第二个 CmemeoryState 对象作为其参数.,如果两次内存快照不相同,则该函数返回非零,说明此程序 段中有内存漏洞

29、。下面我们来看一个典型的例子:/ Declare the variables needed#ifdef _DEBUGCMemoryState oldMemState, newMemState, diffMemState;OldMemState.Checkpoint();#endif/ do your memory allocations and deallocations.CString s = “This is a frame variable“;/ the next object is a heap objectCPerson* p = new CPerson( “Smith“, “Alan“, “581_0215“ );#ifdef _DEBUGnewMemState.Checkpoint();if( diffMemState.Difference( oldMemState, newMemState ) )TRACE( “Memory leaked!n“ );#endif

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报