书城计算机网络Delphi程序设计教程
36209800000024

第24章 程序调试与异常处理

本章要点

程序调试的方法

异常处理机制

在程序设计过程中,无论程序员如何小心,出现错误总是不可避免的,查找并修正这些错误的过程,称为程序的调试。Delphi集成开发环境提供的功能强大的集成调试器,可以帮助程序员快速查找程序中存在的错误。

另外,Delphi提供了异常处理机制,可以使程序在运行中发生异常时,能继续运行而不崩溃。

本章将介绍Delphi的程序调试和异常处理功能。

10.1程序调试

10.1.1程序中的错误类型

程序设计中出现的错误有3种类型:语法错误、运行错误和逻辑错误。

1.语法错误

语法错误,是指编写的程序代码不符合语法规范,如书写错误、未定义变量或数据类型不匹配等。

程序在编译中可以发现语法错误,反白显示错误代码行,并在代码编辑窗口底部指明语法错误的类型。

2.运行错误

运行错误,是指程序能通过编译,但在运行时发生错误,如除数为0、试图打开一个不存在的文件等。

发生运行错误时,程序会停止在出错的语句行上,并显示出错信息。

3.逻辑错误

逻辑错误,是指程序能通过编译并能顺利运行,但运行的结果与设计要求不符。这种错误是由于程序设计的缺陷造成的,是最难发现的一种错误。

调试逻辑错误,要综合运用集成调试器提供的单步、断点、监视数据等手段,找到出错位置、分析出错原因、改正错误。

10.1.2设置调试环境

要使用集成调试器对程序进行调试,必须先对调试环境进行设置。

执行菜单栏中的【Project】→【Options】菜单命令,在打开的【ProjectOptions】对话框中选择【Compiler】选项卡,如图10-1所示。

设置调试环境,就是设置该选项卡的【Debugging】成组框中的各个复选框选项。

1.Debuginformation(调试信息)

该复选框决定集成调试器是否为所编译的程序生成调试信息,并把它放入扩展名为.dcu的调试单元文件中。若需要,应选中该复选框。默认复选框被选中。

2.Localsymbols(局部符号)

该复选框决定集成调试器是否生成程序所使用的局部变量列表,并把它放入扩展名为.dcu的调试单元文件中。若需要,应选中该复选框。默认复选框被选中。

3.Referenceinfo/Definitionsonly(标识符引用∕定义信息)这两个复选框决定编译器是否生成程序所使用的标识符定义、引用信息。若两个复选框都被选中,则编译器只记录标识符的定义信息,使代码浏览器工作;若只选中Referenceinfo复选框,则编译器将记录标识符的定义信息,同时记录标识符在何处使用的信息。默认两个复选框都被选中。

4.Assertions(申明代码)

该复选框决定是否生成申明部分的代码。申明部分的代码在建立可执行文件时可以被删除。若需要,应选中该复选框。默认复选框被选中。

5.UseDebugDCUs(使用调试DCUs)

该复选框决定是否允许程序员以VCL(可视对象库)的调试方式进行调试。若需要,应选中该复选框。默认复选框不被选中。

10.1.3控制程序的运行

集成调试器提供的重要功能之一就是控制程序的运行,这是在程序调试过程中定位程序错误的重要手段。

控制程序运行,就是指程序除正常运行外,还可以用单步运行、设置断点运行、执行到光标处等几种不同的方式运行。

1.单步运行

单步运行,是指每次执行一个程序行,然后暂停,等待下一个执行命令。单步运行又分两种不同的方式。

1)TraceInto

执行菜单栏中的【Run】→【TraceInto】菜单命令,或单击工具栏的【TraceInto】命令按钮,或按下F7快捷键,程序执行一个程序行;当遇到函数或过程时,则进入到函数或过程中继续跟踪。

2)StepOver

执行菜单栏中的【Run】→【StepOver】菜单命令,或单击工具栏的【StepOver】命令按钮,或按下F8快捷键,程序执行一个程序行;当遇到函数或过程时,不进入函数或过程,而是把函数或过程当做一个程序行一次执行完毕,程序暂停在函数或过程后的程序行。

当确信函数或过程本身没有错误时,可以采用这种单步运行方式。

2.设置断点运行

设置断点运行,是将某些程序行设置为断点;程序运行中遇到断点,则暂停在断点处,等待下一个执行命令。

设置为断点的程序行变为红色,并且程序行左边的代码编辑窗口边框处显示一个红色的停止符。一个设置了断点的代码编辑窗口如图10-2所示。

当程序运行到断点暂停后,可以通过分析断点前程序运行的情况,判断程序错误是在断点前还是在断点后,从而缩小查错范围,达到错误迅速定位的目的。

1)断点的设置与取消

设置与取消断点有几种方法。

(1)单击断点程序行左边窗口边框空白处,设置断点;再单击,取消断点。

(2)将光标移到断点程序行,然后按F5键,设置断点;再按F5键,取消断点。

(3)将光标移到断点程序行,单击鼠标右键,在弹出的快捷菜单中选择【Debug】→【ToggleBreakpoint】命令,设置断点;在断点处再执行如上操作,取消断点。

(4)将光标移到断点程序行,执行菜单栏的【Run】→【AddBreakpoint】→【SourceBreakpoint】菜单命令,打开【AddSourceBreakpoint】对话框,如图10-3所示,然后单击【OK】

按钮,设置断点。

2)条件断点的设置

按以上方法设置断点,程序每次执行都会暂停,这在某些情况下是不必要的,而且影响调试效率。如调试一个循环次数为100的循环,每次循环都暂停,显然无法忍受;可以设置前98次不暂停,第99次暂停,这就是所谓的条件断点。

设置条件断点的方法有两种。

(1)若按第4种方法设置断点,当显示图10-3【AddSourceBreakpoint】对话框时,则在【Condition】和【PassCount】编辑框中输入中断条件,然后单击【OK】按钮。

(2)若按前3种方法设置断点,则断点设置完成后,执行菜单栏的【View】→【DebugWindows】→【Breakpoints】菜单命令,打开【BreakpointList】对话框,如图10-4所示。

图10-4【BreakpointList】对话框在对话框中选择要设置成条件断点的断点,单击鼠标右键,在弹出的快捷菜单中选择【Properties】菜单命令,弹出【SourceBreakpointProperties】对话框,如图10-5所示。

在【Condition】和【PassCount】编辑框中输入中断条件,然后单击【OK】按钮。

在图10-3所示的【AddSourceBreakpoint】对话框和图10-5所示的【SourceBreakpointProperties】对话框中,若只有【Condition】编辑框中输入条件表达式,则当表达式的值为True或非0时,才发生中断;若只有【PassCount】编辑框中输入通过次数,则每执行一次,次数减1,当其值为0时,才发生中断;若这两个编辑框同时输入条件,则每执行一次,首先检测【Condition】编辑框中的条件表达式,只有表达式的值为True或非0时,【PassCount】编辑框中的次数才减1,而只有【PassCount】编辑框中的次数为0时,才会发生中断。

3.运行到光标位置

将光标移到欲使程序暂停运行的程序行,然后按F4键,或执行菜单栏中的【Run】→【RuntoCursor】菜单命令,则程序运行到光标所在位置时暂停。

这种运行方式同设置断点运行方式类似,但有以下两点不同。

(1)这种运行方式没有条件设置功能。

(2)设置断点运行方式中,可以一次设置多个断点,每运行到一个断点程序暂停;而这种运行方式,每次运行前只能选择一个光标位置,运行到光标位置后,再选择下一个光标位置,再运行。

10.1.4监视表达式

在程序运行中,监视变量或表达式的值的变化,是集成调试器的另一个重要功能。通过监视,可以帮助程序员分析程序运行的情况。

1.添加监视表达式

要做到对表达式的监视,首先要添加监视表达式,方法如下:

(1)执行菜单栏中的【Run】→【AddWatch】菜单命令,或在代码编辑窗口中单击鼠标右键,在弹出的快捷菜单中选择【Debug】→【AddWatchatCursor】菜单命令,打开【WatchPropeties】对话框,如图10-6所示;

(2)在对话框的【Expression】组合框中直接输入要监视的表达式,或单击组合框右端的下拉箭头,从下拉列表中选择要监视的表达式;

(3)单击【OK】按钮,则选定的监视表达式添加到【WatchList】窗口中,如图10-7所示。

2.打开【WatchList】窗口

除了添加监视表达式时自动打开【WatchList】窗口外,还可以专门打开【WatchList】

窗口,方法为:

执行菜单栏中的【View】→【DebugWindows】→【Watches】菜单命令,则打开如图10-7所示的【WatchList】窗口。

这时运行程序,则可从【WatchList】窗口中,监视到表达式的当前值。

3.删除监视表达式

不需要监视的表达式也可以从监视列表中删除,方法为:

(1)用上面的方法打开如图10-7所示的【WatchList】窗口;(2)在窗口中选择要删除的监视表达式,单击鼠标右键,在弹出的快捷菜单中选择【DeleteWatch】菜单命令,则选择的监视表达式被删除,如图10-8所示。

10.1.5Evaluate/Modify对话框

【Evaluate/Modify】(求值/修改)对话框是集成调试器提供的另一个调试工具,它不仅能像【WatchList】窗口那样监视表达式的值,还能在程序运行中修改变量的值,让该变量以修改后的值完成后续的程序,以观察程序运行情况的变化。

1.打开【Evaluate/Modify】对话框

打开【Evaluate/Modify】对话框有以下几种方法。

同时按下Ctrl+F7组合键。

在代码编辑窗口中单击鼠标右键,弹出快捷菜单,执行【Debug】→【Evaluate/Modify】

菜单命令;执行菜单栏中的【Run】→【Evaluate/Modify】菜单命令;打开的【Evaluate/Modify】对话框如图10-9所示。

2.表达式求值

同【WatchList】窗口的使用类似,配合程序的运行方式,可在【Evaluate/Modify】对话框中对表达式求值或修改表达式的值。

在对话框的【Expression】组合框中直接输入要监视的表达式,或从组合框的下拉列表中选择要监视的表达式,然后单击【Evaluate】命令按钮,则【Result】列表框中显示该表达式的当前值。

3.修改表达式的值

若求值的表达式的值可以修改,则【Newvalue】组合框和【Modify】命令按钮有效,这时可在【Newvalue】组合框中输入表达式的新值,单击【Modify】命令按钮,然后继续运行程序,则程序将以表达式的新值完成后续的程序。

10.2异常处理

若一个错误或特殊事件中断了程序的正常运行过程,就称为异常。

当异常发生时,程序可以通过相应的处理,使程序能继续运行而不崩溃,这种功能称为异常处理机制。

Delphi的异常处理是通过两种结构的语句实现的。

10.2.1try…except语句

try…except语句的语法格式为:

程序先执行try与except之间的语句,若正常执行完毕,则继续执行end后面的程序;若执行try与except之间的语句发生异常,则转入执行except后面的异常处理段。程序会依次查找各个on后面的异常类标识符,若找到匹配的标识符,则执行其do后面的语句,然后跳出try…except语句,继续执行end后面的程序;若找不到匹配的异常类标识符,则执行else后面的语句,然后继续执行end后面的程序。

这段程序将str1和str2转换成两个整数相除,若除数为0,则发生EDivByZero异常,执行相应的异常处理语句j:=StrToInt(str2),保证程序正常运行。

10.2.2try…finally语句

try…finally语句主要用来确保当异常发生时,释放被程序分配的资源,包括文件、内存及Windows资源等。

try…finally语句的语法格式为:

与try…except语句不同的是,不论是否发生异常,finally后面的语句2都会被执行。具体的执行过程是:先执行try后面的语句,若不发生异常,待语句1执行完毕,就执行finally后面的语句2,然后继续执行end后面的程序;若执行语句1时发生异常,则直接跳到finally部分执行语句2。

正常情况下,完成对文件的操作后,应将文件句柄释放。但若在文件操作过程中发生异常,就无法保证文件句柄的释放。

而采用try…finally语句,上面的程序段就成为如下格式:

若不发生异常,这个程序段的执行同上面的程序段完全一样;但若在执行对文件的操作过程中发生异常,则直接跳去执行CloseFile(F)语句,做到无论什么情况,都能保证文件句柄的释放。

习题10

1.程序调试的重要性是什么?程序中有几种类型的错误?如何发现它们?

2.Delphi的集成调试器提供哪几种控制程序运行的手段?各如何操作?

3.什么叫异常处理?Delphi两种进行异常处理的语句是什么?它们具体的执行过程如何?