目录下,则回答D:\\mydir\\tc\\include。
Library directories:库文件目录。回答库文件所在的路径,若Turbo C安装在C盘根目录下的TC子目录下,则回答C:\\tc\\lib。
以上两个操作如回答错误将导致编译程序和连接程序找不到所需文件而出错。
Output directories:输出目录。设定保存编译和连接时所生成的.obj和.exe文件的路径。 5.调试子菜单(Debug)
该菜单主要针对程序调试工作而设,其下拉菜单如图15-7所示。
Inspect:打开Data Inspect对话框,这时可以输入一个要查看或修改其值的数据项或表达式。
图15-7 Debug菜单
Evaluate/modify:对一个变量或表达式求值,并显示
其结果,而且可能的话还允许修改此值。
Call stack:打开包含有调用栈的对话框。其中的CallStack窗口将把程序中执行到当前函数时的函数调用序列显示出来。
Watches:包含所有控制监视点的命令。 Toggle breakpoint:设置或清除断点。
Breakpoints:打开一个可以控制断点使用的对话框。 6.其他子菜单
Turbo C 3.0的主菜单系统还包括系统子菜单(≡)、编辑子菜单(Edit)、搜索子菜单(Search)、工程子菜单(Project)、窗口子菜单(Window)、帮助子菜单(Help)等,分别如图15-8(a)?(f)所示。
(d) 工程子菜单
(e) 窗口子菜单
(f) 帮助子菜单
(a) 系统子菜单
(b) 编辑子菜单
(c) 搜索子菜单
图15-8 Turbo C 3.0的其他子菜单
(1) ≡提供IDE的刷新及转移程序的设置操作功能。
(2) Edit菜单提供剪切(Cut)、拷贝(Copy)及粘贴(Paste)选择块等功能,供编辑窗内编辑文件时使用。
? 292 ?
(3) Search菜单提供在文件中查找、替换文本及查找函数定义等操作功能。
(4) Project菜单提供包括所有工程项目管理命令,如建立一个项目文件、在项目中增加或删除一个文件,设置工程项目选择项,查看项目中某个文件用到的include文件等。
(5) Window菜单提供对所有窗口的管理操作功能。 (6) Help菜单主要提供联机系统帮助功能。
15.4 C程序调试
15.4.1 程序的两种主要错误
1.语法错误
语法错误是指违背语法规定的错误,亦称为编译连接错误。编译连接程序通常能发现这类错误,并给出“出错信息”和出错位置。因此,这类错误比较容易发现并排除。
编译连接错误有致命错误(fatal error)、错误(error)、警告(warning)和连接错误(link error)。致命错误是指导致编译失败的重大错误;错误是指程序编译时出现的所有错误;警告是指编译器对程序中一些不妥当的用法提出的警告,此时,程序虽可以编译连接成可执行文件,但一个正确的程序不仅要求没有错误,能够执行,而且最好在编译时没有警告;连接错误是指在连接时出现的错误,经常是由于函数名书写错误或者连接程序无法找到应该连接的函数而造成的。
2.逻辑错误
逻辑错误是指程序并无语法错误,但不能得到正确的执行结果,由于这类错误具有隐含性,亦称为隐含错误。这是由于程序设计人员编写的程序代码与原意不相同,出现了逻辑上的混乱。例如:
sum?0; i?l;
while (i??10)
sum?sum?i; i??;
该段程序无语法错误,本意是计算1至10的自然数之和,但while语句却仅仅告诉Turbo C:当i??10时执行“sum?sum?i;”语句,而没有包括“i??;”语句。像这种隐含错误就较难发现,这就需要使用一些调试方法和调试技巧。
3.常见错误类型
下面列举了一些初学者常易犯的错误类型,供读者参考、借鉴。 (1) 忘记定义变量。C程序要求对所有的变量都必须先定义后引用; (2) 要输入、输出的数据类型与所用格式说明不一致; (3) 使用scanf函数时忘记使用地址符; (4) 混淆“?”与“??”两个符号的区别; (5) 语句后漏添分号或在不该加的地方加了分号; (6) 复合语句忘记加花括号;
(7) 引用数组元素时误用了圆括号,或将数组的长度当成可引用的最大下标值; (8) 用数组名来替代数组的所有元素,混淆字符数组与字符指针的区别;
? 293 ?
(9) switch语句中漏写break语句; (10) 混淆字符与字符串的表示方法; (11) 没有弄清形参与实参的作用范围; (12) 混用不同类型的指针。
15.4.2 调试程序的一般过程及其调试方法
1.程序调试的一般过程
(1) 人工检查。写好程序后应对程序进行人工检查,找出由于设计人员疏忽而造成的大多数错误。
(2) 上机调试。通过编译连接程序发现语法错误,可根据编译连接时在编译程序信息窗内提示的出错信息,帮助找出程序出错之处。需说明的是,有时提示的出错行并不是真正出错的行,往往出错行在上面的一、二行中。
(3) 运行分析。程序编译连接通过了,并不保证程序完全正确,因为逻辑错误是编译连接程序发现不了的。因此我们应当运行程序,输入程序所需数据,对得到的运行结果进行认真分析,看它是否符合要求。若不符合,则可能是程序有逻辑错误。检查这类错误我们可采用下面的几种方法:
① 检查流程图是否有错,即算法是否正确; ② 对照程序流程图,推敲程序代码是否写错;
③ 在程序适当位置加入一些printf函数调用语句,输出一些中间结果,观察是否正确,逐步缩小错误所在区间;
④ 利用系统提供的debug对程序流程进行跟踪观察。 2.程序调试的方法
Turbo C集成调试器提供了一些易学易用的调试手段,包括:单步跟踪,设置断点,观察计算表达式的值,使用调用栈等。在程序调试过程中,往往是多种方法联合使用,才能把错误排除。
(1) 利用Turbo C集成调试器进行调试的几个条件:
① 在Options|Debugger对话框中将Source debugging开关置为on,这是一个源代码调试开关,设为on时才能使用Turbo C 3.0的集成调试器。
② 要将Options|Compiler|Advanced Code generation中的Debug info in OBJs选中,该设置使得调试信息能放入目标文件中。
③ 建议将Options|Compiler|Entry/Exit Code对话框中的Standard stack frame选中,建立标准栈结构,否则有可能阻止调用栈查看某个函数。
(2) 调试方法:
① 单步跟踪。所谓单步跟踪就是让程序逐条语句执行,每执行一条语句后就将控制权交给调试人员。单步跟踪又分为跟踪进入函数和单步执行两种,分别通过按F7或选择Run选项下的Trace into和按F8或选择Run选项下的Step over来实现。
② 设置断点。用户可以在某条语句位置设置一个断点,当程序执行到该语句时便会停下来,以便调试。断点的设置有永久断点和临时断点两种。永久断点的设置和关闭是通过选择Debug选项下的Toggle breakpoint或直接按Ctrl?F8来实现;临时断点的设置通过选择Run选项下的Goto cursor或直接按F4来实现,临时断点不需要关闭。
? 294 ?
在调试程序时,程序运行出现死锁或陷入死循环,可以使用Ctrl?Break中止程序的运行回到编辑器。
在程序运行中遇到断点时,程序将暂停执行并且回到编辑窗口,光标定位于设置了断点的语句上,若按Ctrl?F9,程序将继续执行;按Ctrl?F2中止调试。
③ 查看调用栈。通过选择Debug选项下的Call stack或直接按Ctrl?F3来实现查看调用栈,利用调用栈来观察函数调用、参数传递以及返回顺序,非常直观有效。
④ 观察和计算表达式的值。在调试过程中经常要观察某些变量、表达式的值,以了解它们的当前取值及变化规律。
通过选择Debug选项下的Evaluate或直接按Ctrl?F4,可以实现观察、计算、修改某个临时表达式,它包括计算域、结果域和新值域三部分。利用它可以对一个临时的表达式或变量的内容进行修改,以观察其变化后的结果。
在调试过程中,如果要随时监视变量或表达式的内容变化,可以使用Debug下的Watches选项进行设置。
3.调试实例
例15.1 输入一行字符,分别统计其中字母、空格及其他字符的个数。 /* 本例仅作调试教学用,它包含了一些错误。*/ #include ?stdio.h? main() {
char c;
int letter?0, space?0, other?0; printf(?Input a string: ?); while (c?getchar()! ??\\n?);
if (c???a? && c???z? || c???A? && c???Z?) /* 判断是否为字母 */
letter??; /* 字母计数 */ else if (c?? ?) /* 判断是否为空格 */
space??; /* 空格计数*/ else /* 否则为其他字符 */
other??; /* 其他字符计数 */
printf(?letter??d, space??d, other??d?, letter, space, other); }
用F9键将该程序编译,信息窗出现两个警告(Warning)信息,提示主函数的第七行和第十行可能是不正确的赋值。首先分析第七行,它的作用应该是:读入一个字符并赋给变量c,然后判断它是否不等于?\\n?。由于运算符! ?的优先级高于运算符?,因此该行的作用实际上是:先判断读入的字符是否不等于?\\n?,然后将判断的结果(“真”或“假”,即1或0)赋给变量c。将表达式c?getchar()!??\\n?改为(c?getchar())!??\\n?即可。再来分析第十行,它的作用应该是:判断变量c是否等于空格。由于此处混淆了赋值运算符?与关系运算符??,因此只需将“?”改为“??”即可。
改正后,再用F9键将该程序编译,信息窗没有出现任何信息。按Ctrl?F9运行,出现提示信息“Input a string: ”后输入一行字符“as 4 @#”后回车,即:
Input a string: as 4 @#?CR?
? 295 ?
运行后,按Alt?F5我们看到的结果是letter和space均为0,而other为1。显然这个结果是错误的,正确的结果应该是:letter、space、other分别为2、2、3。我们用Run下的Step over(F8)或Trace into(F7)来单步跟踪该程序的执行,发现第8行至第13行的if语句只执行了一次“other??;”语句,没有重复执行。由此可以怀疑是循环结构有问题,仔细检查发现第七行行尾有一分号,说明循环语句的循环体为空语句,而不包含下面的if语句。改正的办法是:将第七行行尾的分号删除。
改正后,再用Ctrl?F9运行,按如下方式输入: Input a string: as 4 @#?CR?
运行后,按Alt?F5我们看到的结果是:letter、space、other分别为2、2、3,这个结果是正确的。
例15.2 从键盘输入两个变量的值,然后交换它们的值,并且分别输出交换前后的值。 /* 本例仅作调试教学用,它包含了一些错误。*/ #include ?stdio.h? void temp(int *a, int *b) {
int t; t?a; a?b; b?t; }
void main() {
int i; j;
scanf(??d?d?, i, j);
print(?\\nbefore: i??4d, j??4d?, i, j);
temp(&i, &j);
printf(?\\nafter: i??4d, j??4d?, i, j); }
按F9对它进行编译并连接,发现在编译程序信息窗内有一些错误(Error)信息。一个错误提示为:“Undefined symbol ?j?”,经检查发现main函数的第二行“int i; j;”有错,应改为“int i, j;”才对;另一个错误提示为:“Function ?print? should have a prototype”,很显然是由于printf函数书写错误引起的,改正以后编译连接可以通过了,但还有两个警告信息。
我们先试着运行它,按Ctrl?F9,输入i、j的值,然后按Alt?F5观察运行结果,发现结果不正确。程序错在哪里呢?
我们发现第一个printf语句输出的i、j的值就不正确,这说明scanf 函数调用语句有问题,仔细检查原来是该语句变量i、j前少了地址符&。改正后再编译运行,第二个printf语句输出结果仍然不对。因此一定是temp函数有问题,我们用Run下的Step over(F8)或Trace into(F7)来单步跟踪该程序的执行。按F8使程序执行到scanf函数,然后输入两个数,如2和5。按F8或F7继续,当亮条移动到temp函数时,按Alt?F5可观察到屏幕上输出了第一个printf语句执行的结果,按任意键回到调试状态。此时改按F7跟踪temp函数内各条语句的执行,当执行到赋值“a?b;”后,按Ctrl?F4调试程序打开一个包含三个域的对话框,即计算域、结果域、新值域,在计算域输入t回车后,在结果域得到的值不为2而是一个四位的
? 296 ?