2022年2022年辽宁石油化工大学《C语言程序设计》常见错误和程序调试 .pdf
第 10 章常见错误和程序调试Common error and program debug本章概要Summary of the chapter 程序调试是程序设计过程中一个必不可少的环节,调试过程中的错误处理则是保证程序正确性的必要手段。本章将常见的一些错误列举出来,以方便进行程序调试。C 语言是一种介于高级语言与低级语言之间的中级语言,允许直接访问物理地址,能进行位操作, 可以直接对硬件进行操作。 程序员使用 C 语言编写程序会感到限制少、 灵活性较大、功能强,可以编写出能解决复杂问题的、 运行效率高、占用内存少的高质量程序,不仅用来编写系统软件,也用来编写应用软件,所以得到广泛应用。正因为如此,C 语言受到愈来愈广泛的重视,从初学者到高级软件人员,都在学习C 和使用 C 语言。但是要真正学好、用好C 并不容易。因为 C 语言允许编程人员有较大的自由度,从而放宽了语法检查, C 编译程序对语法的检查不如其他高级语言那样严格,因此,往往要由程序设计者自己设法保证程序的正确性。这就使人感到难以掌握,尤其是初学者,容易出错,出了错还不知什么原因、如何处理。另外,C 语言有些语法规定和其他高级语言不同,学习过其他高级语言的读者往往按照使用其他高级语言的习惯来写C 程序,这也是出错的一个原因。调试一个 C 程序要比调试一个其它高级语言的程序更困难一些。需要不断积累经验,提高程序设计和调试程序的水平。10.1常见错误分析(Common error analyse)程序出错有三种情况:语法错误。由于违背了 C 语言的语法规定而引起的。 如双引号或括号不全、do-while语句缺少 while、使用关键字作变量名等, 对这类错误,编译程序一般都能检测出来, 给出“出错信息” 。并且告诉你在哪一行出错。只要细心,是可以很快发现并排除的。逻辑错误。由于程序的结构或算法错误引起的。程序并没有语法错误,程序运行过程中也没有发生错误,只是最后的运行结果并不是希望的结果。例如,有下面的程序段:main( ) int sum=0, i=1; while(i=100) 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 17 页 - - - - - - - - - 204 第 10 章常见错误和程序调试sum=sum+i; i+; printf(%dn, sum); 语法并无错误。 但程序运行时陷入 “死循环” ,因为 while 循环语句中,起判断条件的表达式“i=100”始终为“真” 。C 系统无法辨别程序中这个语句是否符合设计者的原意,而只能忠实地执行这一指令。 这种错误比语法错误更难检查。 要求程序员有较丰富的经验。运行错误。程序既无语法错误, 也无逻辑错误,但在运行时出现错误甚至停止运行。例如:main( ) int a, b, c; scanf(%d%d, &a, &b); c=b/a; printf(%dn, c); 程序没有语法错误,但是程序运行过程中,如果输入数据时a 的值为 0,就会产生:Division by zero的错误,即在除法运算中“ 0”作了除数。C 程序错误检测的首要工具是编译程序, 它能把语法上的错误找到并分离出来以信息的形式显示,显示的信息分:错误(error)和警告 (warning)。错误(error)将影响程序执行,而警告 (warning)一般不影响程序的执行,但会影响结果的正确性。下面将初学者在学习和使用C 语言时容易犯的错误列举出来, 这些内容在前面各章中大多己谈到,为便于查阅,在本节集中列举,供初学者参考。1数据定义类错误 (Error of data definition classes) (1)忘记定义变量。如有:main( ) x=3; y=6; z=x+y; printf(%dn, z); 编译时将出现 error信息:Undefined symbol xxx 原因是 C 要求对程序中用到的每一个变量都必须先定义后使用, 上面程序中没有对 x、y、z 进行定义。应在函数体的开头加上:int x, y, z; 另外还要注意, C 语言中标识符的大小写是不同的,如果定义与使用时大小写不一致也会出现这样的错误。如有int a=2, b=3, c; c=a+B; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 17 页 - - - - - - - - - 第 10 章常见错误和程序调试 205 系统会认为“ B”没有定义。这是学过BASIC 语言的读者常犯的错误,因为BASIC语言中标识符不分大小写。也可能出现下面情况:Float f; 这也是不合法的, C 语言的关键字不允许大写。(2)定义变量的位置错误。如:main( ) int a, b; scanf(%d%d, &a, &b); int c; c=a+b; printf(%d, c); 编译时将出现 error信息:Expression syntax 原因是将变量定义“ int c; ”放在执行语句后面,系统将其作为一个表达式语句处理,而不再作为变量的定义。(3)定义整型变量时没有考虑其数值范围。如有:int a; a=45678; printf( d,a); 编译时并无错误,但输出的值不是 45678, 而是-19858, 原因是 int 数的范围为-215215-1,即-3276832767 ,45678已超出了 int 型的数值范围。为此可以采用long 型,即改为long int a; a=45678; printf( ld,a); 请注意,如果只定义 a为 long 型,而在输出时仍用“ d”说明符,仍会出现以上错误。(4)混淆字符与字符串的区别。如有:char c=a; 编译时将出现 warning信息:Non-portable pointer assignment 原因是定义的字符变量 c 只能存放一个字符, 但赋给它的值 a是字符串,它包含两个字符a和0。C 语言对字符串按字符数组处理,是一个指针量,所以系统认为程序将一个指针量赋给了一个非指针量。改正时,要么将c 定义为字符数组或指针变量,要么按以下方式给字符变量赋值。 直接赋以字符常量,如: char c=a; 赋以“转义字符”,如:char c=0; 赋以一个字符的 ASCII 码,如: char c=97; (5)曾经定义并给变量赋值,但程序中没有使用该变量。如有:main( ) 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 17 页 - - - - - - - - - 206 第 10 章常见错误和程序调试int a=1, b=2, c=3; printf(%d, %d, a, b); 编译时将出现 warning信息:c is assigned a value which is never used (6)宏定义或文件包含时漏掉了“ #”号。如有:defint PI 3.14159 或include stdio.h 编译时将出现 error信息:Declaration syntax error (7)混淆结构体类型与结构体变量的区别。如有:struct student long int num; char name20; char sex; ; student.num=123; strcpy(student.name, zhangfang); student.sex=M; 编译时将出现 error信息:Undefined symbol student Illegal structure operation 原因是混淆了结构体类型与结构体变量的区别,student是定义的结构体类型而不是结构体变量,直接对结构体类型 student进行操作是非法的。 应当在结构体类型定义的同时或之后,定义一个属于该类型的变量,如:struct student stu; 然后对结构体变量 stu进行相应的操作。2格式类错误 (Error of format classes) (1)语句后面遗漏分号。如有:printf(Good morning?n) 编译时的错误信息为:Statement missing ;原因是分号是 C 程序中语句的必要组成部分, 每个语句必须以分号结束。 这也是与其它高级语言不同的地方。 如果是复合语句, 有些初学者往往遗漏最后一个语句的分号。 如: t=a; a=b; b=t (2)在不该加分号的地方加了分号,如有:for(i=0; i10; i+); scanf(%d, ai); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 17 页 - - - - - - - - - 第 10 章常见错误和程序调试 207 程序并无错误, 但程序的执行结果与原意不符。 程序的本意是用 for 循环输入 10 个整数给一维数组,但在for 后面加上分号后,就单独构成了一个语句,成为一个空循环,实现不了设计思想。这种错误属于逻辑错误。请读者注意,这是一个常见的错误,尤其是在if、for、while 语句中要特别注意。(3)括弧不配对。包括三种情况:花括号、中括号、圆括号不配对。这类错误,纯属粗心所致。花括号不配对,编译将会出现error信息:Compound statement missing 当一个语句中使用多层花括弧时常出现这种错误。中括号不配对,由于形式不同,编译时出现error 信息也不相同,主要在数组的定义或引用中出现这种错误。圆括号不配对,由于形式不同,编译时出现error 信息也各不相同,主要出现在需要使用多层圆括号的复杂表达式中、函数调用中及带圆括号的语句中,像if、switch、for、while、do-while 等语句。如:while(c=getchar( )!=#) putchar(c); 编译时出现 error信息:Call of non-function While statement missing ) 系统认为调用了没有定义的函数并且while 语句后的圆括号不配对。(4)赋值格式错误。如有:b+=a+7; 编译时将出现 error信息:Lvalue required 原因是赋值号“ =”左边应该是变量,而不能是表达式。将表达式a+7 的值赋给表达式 b+是不符合 C 语法规定的。另外,当对符号常量重新赋值时也会出现这种错误。 如有:#define PRICE 30 PRICE=40; 编译系统也会认为“PRICE=40 ” 这个赋值表达式不符合 C语言的语法规定。因为 PRICE是一个符号常量,而不是变量。(5)输入输出的数据的类型与所用格式说明符不一致。如:int a=3; float b=4.5; printf(%f %dn, a, b); 编译时并无出错信息,但运行结果将与原意不符,输出结果为0.000000 l6402 原因是系统并不是按照赋值的规则进行数据转换的(如把 4.5 转换成 4), 而是将数据在存储单元中的具体存放形式按格式符的要求重新组织进行输出的。变量b 在内存中占 4 个字节,输出时只把最后两个字节中的数据按d 格式输出。这属于逻辑上的错误。输入输名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 17 页 - - - - - - - - - 208 第 10 章常见错误和程序调试出数据时一定要注意数据类型与格式说明一致,可改为 printf(%d %fn, a, b); (6)scanf函数中忘记使用地址运算符 &。如:main( ) int a, b,sum; scanf(%d%d, a, b); sum=a+b; printf(sum=%dn, sum); 编译时将出现 warning信息:Possible use of a before definition Possible use of b before definition 原因是系统认为变量 a、b 在参加运算前没有被赋值。用scanf函数给变量输入数据,是要把数据放到变量在内存所占的存储单元中去,如果不使用地址运算符&,就得不到变量的地址,所以变量 a、b就得不到数据。这是许多初学者刚学习 C 语言时一个常见的疏忽, 或者说是习惯性的错误。 因为在其他语言中输入数据时只需写出变量名即可(如BASIC 语言中的 INPUT 语句) ,而 C 语言要求必须指明“向哪个地址标识的单元送值” 。应写成scanf(%d%d, &a, &b); 另外,在程序中直接使用未赋值的变量时,也出现这样的警告信息。如有:int a, b; a=b; (7)输入时数据的组织与要求不符。用 scanf函数输入数据,应注意输入数据的格式,如有:scanf(%d%d,&a, &b); 有人按下面的方法输入数据:3,4 这是错误的。可以用printf(%d%d, a, b); 来验证一下,输出的结果与输入的原始数据是不同的。 输入时数据间应该用空格 (或 Tab键,回车键)来分隔,即应该用以下方法输入:3 4 如果 scanf函数为:scanf(%d,%d, &a,&b); 格式字符串中除了格式说明符外, 对其他字符必须按原样输入。 因此,应按以下方法输入:3,4 此时如果用“ 3 4 ”反而错了。还应注意,不能企图用scanf(input a &b: %d, %d, &a, &b); 想在屏幕上显示一行信息:名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 17 页 - - - - - - - - - 第 10 章常见错误和程序调试 209 input a & b: 然后在其后输入 a和 b 的值,这是不行的,只能写成:printf(input a & b:); scanf(%d,d,a, &b); scanf与 BASIC 语言中 INPUT 语句的功能并不完全相同。(8)混淆赋值号( =)与比较符( = =) 。如有:if(a=b) printf(a equal b); 编译时将出现 warning信息:possibly incorrect assignment 原因是系统认为程序中将一个赋值表达式“a=b”作为 if 语句的条件表达式。这种错误在 if、while 或 do-while 语句中常见。也是学过 BASIC 语言常犯的错误,因为在 BASIC语言中, “=”既可作为赋值号,也可作为关系运算符“等于” ,但在 C 中, “=”是赋值运算符, “= =”才是比较用的关系运算符“等于” 。(9)错误引用寄存器变量的地址。如有:register int a; scanf(%d%d, &a, &b); 或有:register int a; int *p; p=&a; 编译时将出现 error信息:Must take address of memory location 原因是地址运算符 &只能取内存单元的地址,而不能取寄存器变量的地址。寄存器变量没有地址。(10)do语句中少了 while。如有:do sum=sum+n; n+; 编译时将出现 error信息:Do statement must have while (11)do语句后 while 缺少分号do sum=sum+n; n+; while(n=100) 编译时将出现 error信息:Do-while statement missing ; (12)switch 语句的各分支中漏写 break语句。如有:名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 7 页,共 17 页 - - - - - - - - - 210 第 10 章常见错误和程序调试switch(score) case 5:printf(Very good!); case 4 :printf(Good!); ca5c 3 :printf(Pass!); case 2 :printf(Fail!); default:printf(Data error!); 编译时并无错误,但运行结果不是企望的结果。上述switch 语句的作用是希望根据score( 成绩)打印出评语。但当 score的值为 5 时,输出结果为Very Good! Good! Pass! Fail! Data error!原因是漏写了 break语句。case只起标号的作用,而不起判断作用,因此在执行完第一个 printf 函数语句后接着执行第 2、3、4、5 个 printf 函数语句。解决的办法就是在每个case分支的后面加上一条 break语句。这样的错误在编译阶段是发现不了的,只有对执行结果进行分析后才能发现错误。(13)对浮点数进行移位操作。如有:main( ) float a, b=12.4; a=b2; printf(%f, %f, a, b); 编译时将出现 error信息:Illegal use of floating point 原因是对浮点数进行了不合法的运算。3数组类错误 (Error of array classes) (1)直接引用没有定义的数组。如有:main( ) int i; for(i=0; i10; i+) scanf(%d, ai); 编译时将出现 error信息:Invalid indirection 原因是, C 要求对程序中用到的数组必须先定义后使用,上面程序中没有对数组a进行定义就直接引用了数组元素ai是无效。应当在函数体的开头加上数组定义:int a10; 这是学过 BASIC 语言的读者常犯的一个错误。在BASIC 语言中,对元素个数在 10以下的数组可以不经定义直接使用。(2)对数组进行动态定义。如有:int n=10; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 8 页,共 17 页 - - - - - - - - - 第 10 章常见错误和程序调试 211 int an; 编译时将会出现 error 信息:Constant expression required 原因是, C 语言要求定义数组时数组的大小必须是常量或常量表达式。另外,定义二维数组时如果将两个维数都放在一个中括号内,也会出现这种错误。如:int a3, 5; C 语言把方括号中的“ 3,5”看成是一个逗号表达式,这也是学过BASIC 语言的读者常犯的错误。应改为:int a35; 即 C 语言中多维数组的每一维都要放到一个方括号中。(3)引用数组元素时,下标越界。如有:main( ) int a10, int i; for(i=1; iy ?x:y; return z; 编译时将出现 error信息:z not an argument undefined symbol z 原因是系统认为 z 既不是函数 max的参数也不是局部变量。函数的形参与函数内部的局部变量定义位置是不同的,形参应该放在函数体之前定义,而局部变量则应该放在函数体中的说明部分进行定义。因此程序应改为:max(x,y) int x, y; int z; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 10 页,共 17 页 - - - - - - - - - 第 10 章常见错误和程序调试 213 z=xy?x:y; return z; (2)所调用的函数定义在调用语句之后,但在调用之前未加声明。如有:main( ) float x, y, z; x=2.5; y= -5.6; z=max(x, y); printf(%fn, z); float max(float x, float y) return xy?x: y; 编译时将出现 error信息:Type mismatch in redeclaration of max 原因是系统认为 max 类型不对。因为 max 函数是实型的,定义在 main之后, 但在 main函数调用此函数前时却没有声明, 系统默认为是 int 类型的。C 语言中,对被调函数定义在主调函数之后的,除int 类型的函数可以不用声明外,其它都要声明。纠正错误的方法有两种:在 main 函数中增加对 max 函数的声明。float max(float x, float y); 把 max 函数的定义放到 main 函数之前。(3)不要在函数的定义与函数的声明中混用新旧两种模式。如有: main() float fun(float, float); float fun(x, y) float x, y; 编译时将出现Type mismatch in parameter 系统会认为函数 max的参数类型不匹配。(4)错误理解函数的参数传递。如有:void swap(int x, int y) int t; t=x; x=y; y=t; main( ) 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 11 页,共 17 页 - - - - - - - - - 214 第 10 章常见错误和程序调试 int a, b; a=35; b=4; swap(a, b); printf( %d,%dn,a, b); 编译时并没有错误, 但得不到预期的结果, 因为 main函数调用 swap函数属于传值调用,参数传递是单向的, 形参的值发生变化是不会影响实参的, 所以程序想通过调用 swap函数达到 a和 b 交换的目的是实现不了的。如果想使实参的值受到形参的影响,应该用数组名或指针变量作函数的参数,即用地址传递的方式。这样形参的值在被调函数中发生的变化会影响到实参的值,如:void swap(int * pt1, int * pt2) int t; t=*ptl; *ptl=*pt2; *pt2=t; main( ) int a, b, *p1, *p2; a=3; b=4; pl=&a ; p2&b; swap(p1, p2); printf(%d, %dn,a, b); * a 和 b 的值已交换 * 但如果将 swap函数定义成:void swap(int * pt1, int * pt2) int *p; p=ptl; ptl=pt2; pt2=p; 同样也实现不了交换两个数据的目的。 一定要理解函数间参数传递的实质, 都是单向的“值传递” 。(5)函数的实参和形参类型不一致。main( ) int a=3, b=4, c; c=fun(a, b); fun(float x, float y) 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 12 页,共 17 页 - - - - - - - - - 第 10 章常见错误和程序调试 215 编译时并无错误,但得不到正确的结果,因为实参a、b为整型,形参 x、y 为实型。C 要求实参与形参的类型、个数、顺序一致。(6)混淆一般表达式与函数参数的计算次序。如有: int i=5; printf(%d, %d, %dn, i, +i, +i); 许多人认为输出必然是5,6,7 其实不然。在 Turbo C系统中输出的是:7,7,6 因为在该系统中,函数参数的计算次序是从右到左的。先求出最右面一个参数(+i)的值为 6,再求出第 2个参数 (+i)的值为 7,最后求出最左面的参数 (i)的值也是 7。而一般表达式的计算次序是从左到右的。如有:i=3; k=(i+)+(i+)+(i+); 不少人认为 k 的值为 12 即 k=3+4+5,实际上 k 的值为 9,因为系统先将 i 的值取出来作为表达式的值进行加法运算3+3+3,然后再实现自加,即“先用后加”,最后 i 的值变为6。C 语言没有具体规定函数参数求值的顺序是从左到右还是从自右到左。但每个 C 编译程序都有自己的顺序,这就要求在使用中应当避免出现这种容易引起不同理解的用法,以免使程序的通用性受到影响。如果在上例中,希望输出“5,6,7”时,可以改用i=5; j=i+1; k=j+1; printf(%d, %d, %dn, i, j, k); (7)引用 void 类型的函数值。如有:void max(int x, int y) return xy?x: y; 编译时出现 error信息:Type mismatch in redeclaration of max 因为函数的类型定义为 void 后,系统将禁止函数返回任何值。5指针类错误 (Errors of pointer classes) (1)引用没有初始化的指针变量。如有:int *p; *p=10; 编译时将出现 warning信息:possible use of p before definition 原因是虽然定义了指针变量p,但是没有明确指针变量p 的指向,直接引用可能会破坏其它内存单元中的值,甚至造成系统破坏。可以改成:int *p,a; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 13 页,共 17 页 - - - - - - - - - 216 第 10 章常见错误和程序调试p=&a; *p=10; 先将一个变量的地址赋给指针变量p,然后就可以对 p 进行操作了。(2)混淆指针量与非指针量的区别。如有:int *p; p=100; 编译时将出现 warning信息:Non-portable pointer assignment 原因是系统认为程序将一非指针的量赋给一个指针量。因为指针变量只能存放地址,不能将一个整型量(或非地址类型的数据)赋给一个指针变量。同样若将一个指针量给一个非指针量时也会出现这样的警告。如:int a, *p; a=p; (3)不同类型的指针混用。如有:main( ) int i=3, *p1; float a=1.5, *p2; pl&i; p2 &a; p2=p1; printf(d,dn,*p1,*p2); 编译时将出现 warning信息:Suspicious pointer conversion 原因是系统认为程序中出现可疑的指针类型转换。因为在“p2=p1”中,p1、p2 的基类型不同, C 不允许指针变量指向与它类型不同的数据。如果确实需要,必须进行强制类型转换。应改成: p2=(float *)pl; 这种情况在 C 程序中动态分配内存空间时是最常见的。 例如,用 malloc函数开辟内存空间,malloc 函数返回的是不指向任何类型数据的指针(void *) ,而人们希望开辟的是能够存放一个结构体类型变量的值的存储空间。要得到指向该结构体类型变量的指针,必须进行类型转换。如有:#define LEN sizeof(struct student) struct student int num; char name20; float score; ; struct student *p; p=(struct student *)ma11oc(LEN); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 14 页,共 17 页 - - - - - - - - - 第 10 章常见错误和程序调试 217 p是指向 struct studen 结构体类型数据的指针, 将 malloc函数返回的 void * 类型指针转换成指向 struct student 类型变量的指针。(4)混淆数组名与指针变量的区别。如有:main( ) int i,a6; for(i=0;i6;i+) scanf(%d,a+); 编译时将出现 error信息:Lvalue required 原因是系统认为 a+进行了错误的赋值运算。虽然数组名代表数组的首地址,它是一个不能改变的常量,当然不允许做a+运算。而指针变量则可以,应改为:int i,a6,*p; p=a; for(i=0;i6;i+) scanf(%d,p+); 或int *p,a6; for(p=a;pa+6;p+) scanf(%d,p); 6文件操作类 (Error of file operation classes) 使用文件时忘记打开或打开文件方式不对。如:if(fp=fopen(test,r)= =NULL) printf(cannot open this filen);exit(0); ch= fgetc(fp);while(ch!= # ) ch= ch+4;fputc(ch,fp);ch= fgetc(fp) ; 对以“r”方式(只读方式 )打开的文件,进行既读又写的操作,显然是错误的。此外,有的程序常忘记关闭文件, 虽然系统会自动关闭所用文件, 但可能会丢失数据。因此必须在用完文件后关闭它。以上只是列举了一些初学者常出现的错误,这些错误大多是对于C 语法不熟悉之故。对 C 语言使用多了, 比较熟练了,犯这些错误自然减少了。 在深入使用 C 语言后,还会出名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 15 页,共 17 页 - - - - - - - - - 218 第 10 章常见错误和程序调试现其他一些更深入、更隐蔽的错误。10.2程序调试(Program debug)所谓程序调试是指对程序的查错和排错。 写完一个程序只能说完成了任务的一半(甚至不到一半)。调试程序往往比写程序更难, 更需要精力、 时间和经验。有时一个小小的程序会出错五六处,而发现和排除一个错误,有时竞需要半天,甚至更多。希望读者通过实践掌握调试程序的方法和技术。调试程序一般应经过以下几个步骤:1人工检查(静态检查)在写好一个程序以后,不要匆匆忙忙上机,而应对纸面上的程序进行人工检查,这能发现编写程序中的一些错误。为了更有效地进行人工检查,编写程序时应注意力求做到以下几点:(1)采用结构化程序方法编程,使程序结构清晰;(2)尽可能多加注释,对每段程序及主要变量进行说明,增加程序可理解性;(3)采用模块化结构,不要将全部语句都写在main 函数中,要善于利用函数定义及函数调用,用一个函数(模块)来实现一个单独的功能。这样既易于阅读也便于调试。2上机调试通过上机发现错误称动态检查。 在编译时给出语法错误的信息 (包括哪一行有错以及错误类型),可以根据提示的信息具体找出程序中出错之处并改正之。应当注意的是: 有时提示的出错行并不是真正出错的行,如果在提示出错的行上找不到错误的话,应当到上一行再找。另外,有时提示出错的类型并非绝对准确,由于出错的情况繁多而且各种错误互有关联,因此要善于分析,找出真正的错误,而不要只从字面意义上死抠出错信息,钻牛角尖。如果系统提示的出错信息多,应当从上到下逐一改正。有时显示出一大片出错信息往往使人感到问题严重, 无从下手。其实可能只有一二个错误。 例如,若对一个变量未定义,编译时就会对所有使用该变量的语句都发出出错信息。只要加上一个变量定义,所有错误就都消除了。3分析程序运行结果在改正语法错误 (包括“错误” (error)和“警告” (warning)后,程序经过连接 (1ink)就得到可执行的目标程序。运行程序、输入程序所需数据,就可得到运行结果。应当对运行结果进行分析,看它是否符合要求,为此要选择一些模拟数据进行测试。有的初学者看到输出运行结果就认为没问题了,不作认真分析,这是危险的。运行结果不对,大多属于逻辑错误,对这类错误往往需要仔细检查和分析才能发现。可以采用以下办法:(1)将程序与流程图仔细对照。如果流程图是正确的话,程序写错了,是很容易发现的。例如,复合语句忘记写花括号。只要一对照流程图就能很快发现。(2)如果实在找不到错误。可以采取分段检查的方法。在程序不同位置设几个printf函数语句,输出有关变量的值,逐段住下检查直到找到在某一段中数据不对为止。这时就已经把错误局限在这一段中了,不断缩小查错区,就可能发现错误所在。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 16 页,共 17 页 - - - - - - - - - 第 10 章常见错误和程序调试 219 (3)也可以用前面介绍过的“条件编译”命令进行程序调试。(4)如果在程序中没有发现问题,就要检查流程图有无错误,即算法有无问题,如有则改正之,接着修改程序。(5)利用系统提供的调试工具,跟踪流程并给出相应信息,使用更为方便。总之,程序调试是一项细致深入的工作,需要下功夫、动脑子、善于累积经验。希望读者能给以足够的重视。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 17 页,共 17 页 - - - - - - - - -