Delphi应用程序的调试(三)监视变量
监视变量(Watching Variables)
当程序停在一个断点处时,用户做些什么呢?通常用户在断点处停下来是要检查变量的值,某个变量的值是否与预料的取值相同?或者某个变量取什么值(事先并不知道这个变量的取值)。
Watch List窗口的作用是使用户能检查变量的值。编程人员常常忽视这个简单而又根本的特性,因为他们没有花时间来完整地学习调试器的使用。用户可添加任意多的变量到Watch List中。下图就是调试会话期间的Watch List窗口。
变量名和变量值都显示在Watch List中。
Tooltip表达式求值(Tooltip Expression Evaluation)
调试器和Code Editor有一个很好的特性,它使检查变量值的工作变得更容易。这个特性就是Tooltip表达式求值器(expression evaluator),其缺省状态是打开,因此用户不需要做任何事情就能使用它。如果需要,也可以通过Editor Properties对话框的Code Insight页面来关闭它,如下图:
那么,什么是Tooltip表达式求值呢?它是这样工作的:当程序停在一个断点处,用户把编辑光标移到一个变量上,就会弹出一个提示窗口,其中显示该变量的当前值。这样就使快速检查变量的工作变得更容易,只需将光标移到变量上并稍等片刻。
Tooltip evaluator求值器对于不同的变量类型由不同的显示。对于常规数据成员(Integer、Char、Byte、String等等),显示变量的实际值;对于动态创建的对象(例如一个类的实例),Tooltip evaluator显示记录的全部元素。如下图所示:
Note
有时候Tooltip evaluator好像工作不正常。例如,若将编辑光标移到一个作用域外的变量上时,就不会显示提示窗口,因为Tooltip evaluator 无任何信息可显示。被编辑器优化过的变量也可能不能显示正确值。
当光标位于with块中时,Tooltip evaluator也不工作。例如,看下面这段代码:
var pp: Point; begin with pp do begin x := 20; y := 40; end; Caption := IntToStr(pp.x); end;如果把光标放到变量x上,Tooltip evaluator不会显示x的值,因为x属于with语句(Point变量)。如果将光标移到变量Point上,调试器会显示Point的值(包括x字段)。如下图:
Tooltip 表达式求值器很有用的,别忘记使用它。
Watch List快捷菜单(The Watch List Context Menu)
正如前面讲到的每一个Delphi窗口一样,Watch List也有它自己的快捷菜单。如下描述:
Edit Watch——打开Watch Properties对话框修改监视项属性
Add Watch——打开Watch Properties对话框新监视项
Enable Watch——启用一个已经禁用的监视项
Disable Watch——禁用一个已启用的监视项
Delete Watch——删除一个监视项
Enable All Watches——启用所有已经禁用的监视项
Disable All Watches——禁用所有已经启用的监视项
Delete All Watches——删除所有监视项
Add Group——增加一个监视组
Delete Group——删除一个监视组
Move Watch to Group——移动监视项到组
Stay On Top——使Watch List窗口始终在最上层
Inspect——显示选中的监视项的有关信息
Break When Changed——当监视窗口中的变量改变时,调试器将终端。监视变量以红色显示,表示Break When Changed起作用。
Dockable——窗口是可泊位。
使用Watch Properties对话框(Using the Watch Properties Dialog Box)
当用户增加或编辑监视项时,要使用Watch Properties对话框。如上图显示的是正在编辑pp变量的Watch Properties对话框。
位于对话框顶部的【Expression字段】,是用户输入要编辑或增加到Watch List中的变量名的地方,这个字段是一个组合框,可以用来选取以前用过的监视项。
但用户要检查数组时,使用【Repeat count字段】,比如,有一个20个整数元素的数组,要检查数组的头10个整数,可在【Expression字段】中输入数组的第一个元素(例如Array[0]),再在【Repeat count字段】中输入10。数组的头10个元素就会显示在Watch List中。如下图pp为有20个元素的整型数组:
Note
如果将数组名增加到Watch List中,则数组的全部元素都会显示在Watch List中。当要查看数组的部分元素时,使用【Repeat count字段】。
仅当检查浮点数时,采使用【Digits字段】;在此输入显示浮点数时要显示的有效位数,有效位后面的余数作四舍五入处理。【Enabled字段】确定监视项是否当前启用。
Watch Properties对话框上的其余选项都是显示选项。每一种数据类型都有一种缺省显示类型,当用户选中【Default】时,就使用缺省显示类型,Default查看选项的默认状态时“选中”的。选择其他的查看选项,就以其他方式查看数据。如下图:
要修改一个监视项,可在Watch List中点击该项,然后在右键快捷菜单中选择【Edit Watch…】菜单项,也可双击该监视项,快速打开Watch Properties对话框。
启用和禁用监视项(Enabling and Disabling Watch Items)
就像使用断点时一样,Watch List中每项都可被启用或禁用。当一个监视项被禁用时,它会变灰,并且它的值显示为<disabled>。如下图:
要禁用一个监视项,可点击Watch List中该项的名字,并从Watch List快捷菜单中选择【Disabled Watch】菜单项;要启用该监视项,就从快捷菜单中选【Enabled Watch】菜单项。
Note
用户可能希望禁用当前不使用但以后要使用的监视项。在调试过程中,Watch List中的许多有效监视项会减慢程序执行速度,因为每当一个代码行执行时,所有Watch List变量都要更新。
向Watch List中添加变量(Adding Variables to the Watch List)
可用多种方法往Watch List中添加监视项。最快速的方法是点击Code Editor中的变量名,然后从Code Editor的快捷菜单中选择【Debug | Add Watch at Cursor】或按【Ctrl + F5】键,该监视项就会被立即增加到Watch List中。如果需要,用户可以编辑该监视项,修改其属性。
要增加一个变量到Watch List中而又不想在源文件中找出该变量时,可从主菜单选择【Run | Add Watch】菜单项;当Watch Properties对话框弹出后,输入要增加到Watch List中的变量名并点击OK。
Note
虽然可将类实例变量添加到Watch List中,但显示的值可能无用。如下图:
应该使用Debug Inspector查看类的所有数据成员。
使用Watch List(Using the Watch List)
当碰到断点时,Watch List显示其中所有变量的当前值,如果当前没有打开Watch List窗口,可从主菜单【View | Debug Window | Watches】菜单项打开Watch List窗口。
Note
将Watch List窗口泊位到Code Editor窗口的底部,这样,在调试代码时,总可以看到它。
在某些情况下,Watch List中变量的后面会显示一条消息,而不是该变量的值。例如,如果一个变量不在作用域之外,或未找到,Watch List在该变量名后面显示“Undeclared identifier: ‘X’”;如果程序没有运行或不停在断点处,Watch List在所有的监视项后都显示[process not accessible]。禁用监视项后显示<disabled>。根据应用程序的当前状态或某个变量的当前状态,还可能显示其他消息。
就像上一章提到过的,用户在Watch List中偶尔会看到这样的显示:variable 'X' inaccessible here due to optimization,这是带优化功能的编辑器的一个小缺点。如果要检查易被优化的变量,则必须关闭编译器的优化功能,即把Project Options对话框中的Compiler页面上的Optimization选项关闭。
未被初始化的变量会显示随机值,直到它被初始化。
Note
Watch List可用作一个简单的十进制/十六进制转换器。要把一个十六进制数转换成十进制数,可从主菜单选择【Run | Add Watch】,在Expression字段输入十六进制数后点击OK,输入的十六进制数和与之等价的十进制数就会显示在Watch List中;要将一个十进制数转换成十六进制数,除点击Hexadecimal单选钮将显示类型改为十六进制外,其余操作都与前面相同。由于Expression接受数学表达式,因此还可以把Watch List当做一个十六进制计算器,甚至可以在同一个表达式中同时使用十六进制和十进制值。如下图:
这样使用Watch List只有一个缺点,应用程序必须停在一个断点处。
在调试应用程序的过程中,Watch List是一个简单而又关键的工作。为了说明Watch List的使用,接下来做一个练习:
1)创建一个应用程序,在其主窗体上放置一个按钮。将按钮的Name属性设置为WatchBtn,Caption属性改为Watch Test。将主窗体Name属性改为DebugMain,Caption属性改为Debug Watch List Test。
2)双击按钮,会在Code Editor中显示按钮的OnClick处理事件,按下面的样子修改其OnClick处理程序:
procedure TDebugMain.WatchBtnClick(Sender: TObject); var S: string; X, Y: Integer; begin X := Width; S := IntToStr(X); Y := Height; X := X * Y; S := IntToStr(X); X := X div Y; S := IntToStr(X); Width := X; Height := Y; end;
3)保存该工程,将单元命名为DbgMain,工程命名为DebugTest。
4)在OnClick处理程序的begin语句之后的第一行上设置一个断点,运行该应用程序。
5)点击Watch Test按钮,调试器会停止在断点处。当调试器停在断点处时,IDE和Code Editor会显示在最顶层。
6)把变量S,X和Y增加到Watch List中(由于代码优化,变量X和变量Y在开始时是不可访问的),如下图:
7)安排好Watch List和Code Editor的位置,以便用户能同时看到两个窗口(不妨把Watch List泊位到Code Editor的底部)。
8)将输入焦点切换到Code Editor,并按F8执行下一行代码,执行完毕后,执行点移到下一行。此时Watch List中变量X显示一个值。如下图:
9)按F8继续一行一行执行程序,监视Watch List中变量的结果。
10)当执行点到达OnClick处理程序的最后一行时,点击工具栏中的Run按钮继续运行应用程序。
用户可反复点击Watch Test按钮来体会Watch List是如何工作的,每次还可试验不同监视设置的效果。
Note
在上面的例子中,OnClick处理程序先获取主窗体的Width属性和Height属性的值,接着执行一些计算,然后再将Width和Height设置成开始时的值。这段程序执行完后,什么都未发生改变。在该方法末尾给Width和Height属性赋值是有原因的。
如果在代码中不真正使用变量X和Y的话,用户就不能检查这两个变量,因为编译器在优化代码时会发现这个两个变量未被使用,从而将它们删除。在方法的末尾使用这两个变量就是为了避免编译器把它们优化掉。
前面提到过这个问题,希望大家对带优化功能的编译器的工作过程有个基本了解。这样,当开始调试应用程序,碰到诸如“Variable ‘Y’ inaccessible here due to optimization”一类的消息时就不会不知所措。
以上代码均在Delphi7中测试通过,实例代码下载:WatchList测试程序.rar
posted on 2012-05-25 14:32 pchmonster 阅读(11486) 评论(1) 编辑 收藏 举报