常见错误和程序调试课件.ppt

上传人(卖家):晟晟文业 文档编号:4582136 上传时间:2022-12-21 格式:PPT 页数:60 大小:164.50KB
下载 相关 举报
常见错误和程序调试课件.ppt_第1页
第1页 / 共60页
常见错误和程序调试课件.ppt_第2页
第2页 / 共60页
常见错误和程序调试课件.ppt_第3页
第3页 / 共60页
常见错误和程序调试课件.ppt_第4页
第4页 / 共60页
常见错误和程序调试课件.ppt_第5页
第5页 / 共60页
点击查看更多>>
资源描述

1、第第1616章章 常见错误和程序调试常见错误和程序调试16.1常见错误分析常见错误分析16.2程序调试程序调试 要真正学好要真正学好C、用好、用好C并不容易,并不容易,“灵活灵活”固然是固然是好事,但也使人难以掌握,尤其是初学者往往出好事,但也使人难以掌握,尤其是初学者往往出了错还不知怎么回事。了错还不知怎么回事。C编译程序对语法的检查编译程序对语法的检查不如其他高级语言那样严格不如其他高级语言那样严格(这是为了给程序人员这是为了给程序人员留下留下“灵活灵活”的余地的余地)。因此,往往要由程序设计。因此,往往要由程序设计者自己设法保证程序的正确性。调试一个者自己设法保证程序的正确性。调试一个C

2、程序程序要比调试一个要比调试一个PASCAL或或FORTRAN程序更困难程序更困难一些。需要不断积累经验,提高程序设计和调试一些。需要不断积累经验,提高程序设计和调试程序的水平。程序的水平。C语言有些语法规定和其他高级语言不同,学习语言有些语法规定和其他高级语言不同,学习过其他高级语言的读者往往按照使用其他高级语过其他高级语言的读者往往按照使用其他高级语言的习惯来写言的习惯来写C程序,这也是出错的一个原因。程序,这也是出错的一个原因。16.1常见错误分析常见错误分析 下面将初学者在学习和使用下面将初学者在学习和使用C语言语言(不包括不包括C+)时时容易犯的错误列举出来,以起提醒的作用。这些容易

3、犯的错误列举出来,以起提醒的作用。这些内容在以前各章中大多已谈到,为便于查阅,在内容在以前各章中大多已谈到,为便于查阅,在本章中集中列举,供初学者参考,以此为鉴。本章中集中列举,供初学者参考,以此为鉴。(1)忘记定义变量。如:忘记定义变量。如:main()x=3;y=6;printf(%dn,x+y);C要求对程序中用到的每一个变量都必须定义其要求对程序中用到的每一个变量都必须定义其类型,上面程序中没有对类型,上面程序中没有对x、y进行定义。应在函进行定义。应在函数体的开头加数体的开头加intx,y;这是学过这是学过BASIC和和FORTRAN语言的读者写语言的读者写C程序时常见的一个错误。程

4、序时常见的一个错误。在在BASIC语言中,可以不必先定义变量类型就可语言中,可以不必先定义变量类型就可直接使用。在直接使用。在FORTRAN中,未经定义类型的变中,未经定义类型的变量按隐含的量按隐含的I-N规则决定其类型,而规则决定其类型,而C语言则要求语言则要求对用到的每一个变量都要在本函数中定义对用到的每一个变量都要在本函数中定义(除非已除非已定义为外部变量定义为外部变量)。(2)输入输出的数据的类型与所用格式说明符不一输入输出的数据的类型与所用格式说明符不一致。致。例如,若例如,若a已定义为整型,已定义为整型,b已定义为实型。已定义为实型。a=3;b=45;printf(%f%dn,a,

5、b);编译时不给出出错信息,但运行结果将与原意不编译时不给出出错信息,但运行结果将与原意不符,输出为符,输出为 000000016402 它们并不是按照赋值的规则进行转换它们并不是按照赋值的规则进行转换(如把如把45转转换成换成4),而是将数据在存储单元中的形式按格式,而是将数据在存储单元中的形式按格式符的要求组织输出符的要求组织输出(如如b占占4个字节,只把最后两个个字节,只把最后两个字节中的数据按字节中的数据按%d,作为整数输出,作为整数输出)。(3)未注意未注意int型数据的数值范围。型数据的数值范围。一般微型计算机上使用的一般微型计算机上使用的C编译系统,对一个整编译系统,对一个整型数

6、据分配两个字节。因此一个整数的范围为型数据分配两个字节。因此一个整数的范围为-215215-1,即,即-3276832767。常见这样的程序。常见这样的程序段:段:int num;num=89101;printf(%d,num);得到的却是得到的却是23565,原因是,原因是89101已超过已超过32767。两。两个字节容纳不下个字节容纳不下89101,则将高位截去。见图,则将高位截去。见图16.1。即将超过低即将超过低16位的数截去。即将位的数截去。即将89101减去减去216(即即16位二进制所形成的模位二进制所形成的模)。89101-65536=23565。图图16.1 有时还会出现负数

7、。例如有时还会出现负数。例如 num=196607;输出得输出得-1。因为。因为196607的二进制形式为的二进制形式为 00 00 00 00 00 00 00 1011 11 11 11 11 11 11 11 去掉高位去掉高位10,低,低16位的值是位的值是-1(-1的补码是:的补码是:1111111111111111)。对于超过整个范围的数,要用对于超过整个范围的数,要用long型,即改为型,即改为 long intnum;num=89101;printf(%ld,num);请注意,如果只定义请注意,如果只定义num为为long型,而在输出时型,而在输出时仍用仍用“%d”说明符,仍会出

8、现以上错误。说明符,仍会出现以上错误。(4)输入变量时忘记使用地址符。如:输入变量时忘记使用地址符。如:scanf(%d%d,a,b);这是许多初学者刚学习这是许多初学者刚学习C语言时一个常见的疏忽,语言时一个常见的疏忽,或者说是习惯性的错误,因为在其他语言中在输或者说是习惯性的错误,因为在其他语言中在输入时只需写出变量名即可,而入时只需写出变量名即可,而C语言要求指明语言要求指明“向哪个地址标识的单元送值向哪个地址标识的单元送值”。应写成。应写成scanf(%d%d,&a,&b);(5)输入时数据的组织与要求不符。输入时数据的组织与要求不符。用用scanf函数输入数据,应注意如何组织输入函数

9、输入数据,应注意如何组织输入数据。假如有以下数据。假如有以下scanf函数:函数:scanf(%d%d,&a,&b);有人按下面的方法输入数据:有人按下面的方法输入数据:3,4 这是错的。数据间应该用空格来分隔。读者可以这是错的。数据间应该用空格来分隔。读者可以用用 printf(%d%d,a,b);来验证一下。应该用以下方法输入:来验证一下。应该用以下方法输入:34 如果如果scanf函数为函数为 scanf(%d,%d,&a,&b);对对scanf函数中格式字符串中除了格式说明符外,函数中格式字符串中除了格式说明符外,对其他字符必须按原样输入。因此,应按以下方对其他字符必须按原样输入。因此

10、,应按以下方法输入:法输入:3,4 此时如果用此时如果用“34”反而错了。还应注意,不能企图反而错了。还应注意,不能企图用用 scanf(input a&b:%d,%d,&a,&b);想在屏幕上显示一行信息:想在屏幕上显示一行信息:input a&b:然后在其后输入然后在其后输入a和和b的值,这是不行的。这是由的值,这是不行的。这是由于有的读者以为于有的读者以为scanf具有具有BASIC语言中的语言中的INPUT语句的功能语句的功能(先输出一个字符串,再输入变量的值先输出一个字符串,再输入变量的值)。如果想在屏幕上得到所需的提示信息,可以另加如果想在屏幕上得到所需的提示信息,可以另加一个一个

11、printf函数语句:函数语句:printf(input a&b:);scanf(%d,%d,&a,&b);(6)误把误把“=”作为作为“等于等于”比较符。比较符。在许多高级语言中,用在许多高级语言中,用“=”符号作为关系运算符符号作为关系运算符“等于等于”。例如,在。例如,在BASIC或或PASCAL程序中都程序中都可以写可以写 if(a=b)then 但在但在C语言中,语言中,“=”是赋值运算符,是赋值运算符,“=”才是关才是关系运算符系运算符“等于等于”。如果写成。如果写成 if(a=b)printf(a equal to b);C编译系统将编译系统将(a=b)作为赋值表达式处理,将作为

12、赋值表达式处理,将b的值的值赋给赋给a,然后判断,然后判断a的值是否零,若为非零,则作的值是否零,若为非零,则作为为“真真”;若为零作为假。如果若为零作为假。如果a的值为的值为3,b的值的值为为4,ab,按原意不应输出,按原意不应输出“ae q u a lt ob”。而现在。而现在先将先将b的值赋给的值赋给a,a也为也为4,赋值表达式的值为,赋值表达式的值为4。if语句中的表达式值为真语句中的表达式值为真(非零非零),因此输出,因此输出“ae q u a lt o b”。这种错误在编译时是检查不出来的,但运行结果这种错误在编译时是检查不出来的,但运行结果往往是错的。而且由于习惯的影响,程序设计

13、者往往是错的。而且由于习惯的影响,程序设计者自己往往也不易发觉。自己往往也不易发觉。(7)语句后面漏分号。语句后面漏分号。C语言规定语句末尾必须有分号。分号是语言规定语句末尾必须有分号。分号是C语句不语句不可缺少的一部分。这也是和其他语言不同的。有可缺少的一部分。这也是和其他语言不同的。有的初学者往往忘记写这一分号。如:的初学者往往忘记写这一分号。如:a=3 b=4 编译时,编译程序在编译时,编译程序在“a=3”后面未发现分号,就后面未发现分号,就把下一行把下一行“b=4”也作为上一行的语句的一部分,也作为上一行的语句的一部分,这就出现语法错误。有时编译时指出某行有错,这就出现语法错误。有时编

14、译时指出某行有错,但在该行上并未发现错误,应该检查上一行是否但在该行上并未发现错误,应该检查上一行是否漏了分号。漏了分号。如果用复合语句,有的学过如果用复合语句,有的学过PASCAL语言的读者语言的读者往往漏写最后一个语句的分号,如:往往漏写最后一个语句的分号,如:t=a;a=b;b=t 在在PASCAL中分号是两个语句间的分隔符而不是语中分号是两个语句间的分隔符而不是语句的一部分,而在句的一部分,而在C中,没有分号的就不是语句。中,没有分号的就不是语句。(8)在不该加分号的地方加了分号。在不该加分号的地方加了分号。例如:例如:if(ab);printf(aislarger than bn);

15、本意为当本意为当ab时输出时输出“aislarger than b”的信的信息。息。但由于在但由于在if(ab)后加了分号,因此后加了分号,因此if语句到此结束。语句到此结束。即当即当(ab)为真时,执行一个空语句。本来想为真时,执行一个空语句。本来想ab时不输出上述信息,但现在时不输出上述信息,但现在printf函数语句并不从函数语句并不从属于属于if语句,而是与语句,而是与if语句平行的语句。不论语句平行的语句。不论ab还是还是ab,都输出,都输出“a is larger than b”。又如:又如:for(i=0;i10;i+);scanf(%d,&x);printf(%dn,x*x);

16、本意为先后输入本意为先后输入10个数,每输入一个数后输出它个数,每输入一个数后输出它的平方值。由于在的平方值。由于在for()后加了一个分号,使循后加了一个分号,使循环体变成了空语句。只能输入一个整数并输出它环体变成了空语句。只能输入一个整数并输出它的平方值。的平方值。总之,在总之,在if、for、while语句中,不要画蛇添足多语句中,不要画蛇添足多加分号。加分号。(9)对应该有花括弧的复合语句,忘记加花括弧。对应该有花括弧的复合语句,忘记加花括弧。如:如:sum=0;i=1;while(i=100)sum=sum+i;i+;本意是实现本意是实现1+2+100,即,即i。但上面的语句只。但上

17、面的语句只是重复了是重复了sum+1的操作,而且循环永不终止。因为的操作,而且循环永不终止。因为i的值始终没有改变。错误在于没有写成复合语句的值始终没有改变。错误在于没有写成复合语句形式。因此形式。因此while语句的范围到其后第一个分号为语句的范围到其后第一个分号为止。语句止。语句“i+;”不属于循环体范围之内。应改不属于循环体范围之内。应改100i=0为为while(i=100)sum=sum+i;i+;(10)括弧不配对。括弧不配对。当一个语句中使用多层括弧时常出现这类错当一个语句中使用多层括弧时常出现这类错误,纯属粗心所致。如:误,纯属粗心所致。如:while(c=getchar()!

18、=#)putchar(c);少了一个右括弧。少了一个右括弧。(11)在用标识符时,忘记了大写字母和小写字母在用标识符时,忘记了大写字母和小写字母的区别。的区别。例如:例如:main()inta,b,c;a=2;b=3;C=A+B;printf(%d+%d=%,A,B,C);编译时出错。编译程序把编译时出错。编译程序把a和和A认作是两个不同的认作是两个不同的变量名处理,同样变量名处理,同样b和和B,c和和C都分别代表两个不都分别代表两个不同的变量。同的变量。(12)引用数组元素时误用了圆括弧。引用数组元素时误用了圆括弧。如:如:main()int i,a(10);for(i=0;i10;i+)s

19、canf(%d,&a(i);C语言中对数组的定义或引用数组元素时必须用语言中对数组的定义或引用数组元素时必须用方括弧。方括弧。(13)在定义数组时,将定义的在定义数组时,将定义的“元素个数元素个数”误认误认为是为是“可使用的最大下标值可使用的最大下标值”。main()int a10=1,2,3,4,5,6,7,8,9,10;int i;for(i=1;i=10;i+)printf(%d,ai);想输出想输出a1到到a10。但一些初学者常犯的错误。但一些初学者常犯的错误。C语言规定定义时用语言规定定义时用a10,表示,表示a数组有数组有10个元素,个元素,而不是可以用的最大下标值为而不是可以用的

20、最大下标值为10。数组只包括。数组只包括a0到到a910个元素,因此用个元素,因此用a10就超出就超出a数组的范围数组的范围了。了。(14)对二维或多维数组的定义和引用的方法不对。对二维或多维数组的定义和引用的方法不对。main()int a5,4;printf(%d,a1+2,2+2);对二维数组和多维数组在定义和引用时必须将每对二维数组和多维数组在定义和引用时必须将每一维的数据分别用方括弧括起来。上面一维的数据分别用方括弧括起来。上面a5,4应应改为改为a54,a1+2,2+2应改为应改为a1+22+2。根。根据据C的语法规则,在一个方括弧中的是一个维的的语法规则,在一个方括弧中的是一个维

21、的下标表达式,下标表达式,a1+2,2+2中方括弧中的中方括弧中的“1+2,2+2”是一个逗号表达式,它的值是第二个数值表达式是一个逗号表达式,它的值是第二个数值表达式的值,即的值,即2+2的值为的值为4。所以。所以a1+2,2+2相当于相当于a4。而。而a4是是a数组的第数组的第4行的首地址。因此执行行的首地址。因此执行printf函数输出的结果并不是函数输出的结果并不是a34的值,而是的值,而是a数组第数组第4行的首地址。行的首地址。(15)误以为数组名代表数组中全部元素。误以为数组名代表数组中全部元素。例如:例如:main()int a4=1,3,5,7;printf(%d%d%d%dn

22、,a);企图用数组名代表全部元素。在企图用数组名代表全部元素。在C语言中,数组语言中,数组名代表数组首地址,不能通过数组名输出名代表数组首地址,不能通过数组名输出4个整数。个整数。(16)混淆字符数组与字符指针的区别。混淆字符数组与字符指针的区别。main()char str4;str=Computer and c;printf(%sn,str);编译出错。编译出错。str是数组名,代表数组首地址。在编是数组名,代表数组首地址。在编译时对译时对str数组分配了一段内存单元,因此在程序数组分配了一段内存单元,因此在程序运行期间运行期间str是一个常量,不能再被赋值。因此,是一个常量,不能再被赋值

23、。因此,str=“Computer and c”是错误的。如果把是错误的。如果把“char str4;”改成改成“charstr;”,则程序正确。此时,则程序正确。此时str是指向字符数据的指针变量,是指向字符数据的指针变量,str=“Computer and c”是合法的,它将字符串的首地址赋给指针变量是合法的,它将字符串的首地址赋给指针变量str,然后在,然后在printf函数语句中输出字符串函数语句中输出字符串“Computer and c”。因此应当弄清楚字符数组与字符指针变量用法的因此应当弄清楚字符数组与字符指针变量用法的区别。区别。(17)在引用指针变量之前没有对它赋予确定的值。在

24、引用指针变量之前没有对它赋予确定的值。main()char*p;scanf(%s,p);没有给指针变量没有给指针变量p赋值就引用它,编译时给出警告赋值就引用它,编译时给出警告信息。应当改为信息。应当改为charp,c20;p=c;scanf(%s,p);即先根据需要定义一个大小合适的字符数组即先根据需要定义一个大小合适的字符数组c,然,然后将后将c数组的首地址赋给指针变量数组的首地址赋给指针变量p,此时,此时p有确定有确定的值,指向数组的值,指向数组c。再执行。再执行scanf函数就没有问题了,函数就没有问题了,把从键盘输入的字符串存放到字符数组把从键盘输入的字符串存放到字符数组c中。中。(1

25、8)switch语句的各分支中漏写语句的各分支中漏写break语句。语句。例如:例如:switch(score)case 5:printf(ery good!);case 4:printf(Good!);case 3:printf(Pass!);case 2:printf(Fail!);defult:printf(data error!);上述上述switch语句的作用是希望根据语句的作用是希望根据score(成绩成绩)打印打印出评语。但当出评语。但当score的值为的值为5时,输出为时,输出为 ery Good!Good!Pass!Fail!data error!原因是漏写了原因是漏写了br

26、eak语句。语句。case只起标号的作用,只起标号的作用,而不起判断作用,因此在执行完第一个而不起判断作用,因此在执行完第一个printf函数函数语句后接着执行第语句后接着执行第2、3、4、5个个printf函数语句。函数语句。应改为应改为switch(score)case 5:printf(erygood!);break;case 4:printf(Good!);break;case 3:printf(Pass!);break;case 2:print(Fail!);break;defult:print(data error!);(19)混淆字符和字符串的表示形式。混淆字符和字符串的表示形式

27、。char sex;sex=M;sex是字符变量,只能存放一个字符。而字符常量是字符变量,只能存放一个字符。而字符常量的形式是用单引号括起来的,应改为的形式是用单引号括起来的,应改为 sex=M;“M”是用双引号括起来的字符串,它包括两个字是用双引号括起来的字符串,它包括两个字符:符:M和和0,无法存放到字符变量,无法存放到字符变量sex中。中。(20)使用自加使用自加(+)和自减和自减(-)运算符时出的错误。运算符时出的错误。例如:例如:main()intp,a5=1,3,5,7,9;p=a;printf(%d,*p+);不少人认为不少人认为“*p+”的作用是先使的作用是先使p加加1,即指向

28、第,即指向第1个元素个元素a1处,然后输出第一个元素处,然后输出第一个元素a1的值的值3。其实应该是先执行其实应该是先执行p+,而,而p+的作用是先用的作用是先用p的原的原值,用完后使值,用完后使p加加1。p的原值指向数组的原值指向数组a的第的第0个元个元素素a0,因此因此*p就是第就是第0个元素个元素a0的值的值1。结论是先输出。结论是先输出a0的值,然后再使的值,然后再使p加加1。如果是。如果是*(+p),则先使,则先使p指向指向a1,然后输出,然后输出a1的值。的值。(21)有人习惯用传统的方式对函数形参进行声明,有人习惯用传统的方式对函数形参进行声明,但却把对函数的形参和函数中的局部变

29、量混在一但却把对函数的形参和函数中的局部变量混在一起定义。如:起定义。如:max(x,y)int x,y,;=xy?x,y;return();应改为应改为max(x,y)int x,y;int;=xy?x:y;return();(22)所调用的函数在调用语句之后才定义,而又所调用的函数在调用语句之后才定义,而又在调用前未加说明。在调用前未加说明。main()float x,y,;x=35;y=-76;=max(x,y);printf(%fn,);float max(float x,float y)return(=xy?x:y);这个程序乍看起来没有什么问题,但在编译时有这个程序乍看起来没有什么

30、问题,但在编译时有出错信息。原因是出错信息。原因是max函数是实型的,而且在函数是实型的,而且在main函数之后才定义,也就是函数之后才定义,也就是max函数的定义位函数的定义位置在置在main函数中的调用函数中的调用max函数之后。改错的方函数之后。改错的方法可以用以下二者之一:法可以用以下二者之一:在在main函数中增加一个对函数中增加一个对max函数的声明,函数的声明,即函数的原型:即函数的原型:main()float max(float,float);/*声明将要用到的声明将要用到的max函数为实型函数为实型*/float x,y,;x=35;y=-76;=max(x,y);print

31、f(%fn,);将将max函数的定义位置调到函数的定义位置调到main函数之前。即:函数之前。即:float max(float x,float y)return(=xy?x:y);main()float x,y,;x=35;y=-76;=max(x,y);printf(%fn,);这样,编译时不会出错,程序运行结果是正确的。这样,编译时不会出错,程序运行结果是正确的。(23)误认为形参值的改变会影响实参的值。误认为形参值的改变会影响实参的值。main()inta,b;a=3;b=4;swap(a,b);printf(%d,%dn,a,b);swap(int x,int y)int t;t=x

32、;x=y;y=t;原意是通过调用原意是通过调用swap函数使函数使a和和b的值对换,然后的值对换,然后在在main函数中输出已对换了值的函数中输出已对换了值的a和和b。但是这样。但是这样的程序是达不到目的的,因为的程序是达不到目的的,因为x和和y的值的变化是的值的变化是不传送回实参不传送回实参a和和b的,的,main函数中的函数中的a和和b的值并的值并未改变。未改变。如果想从函数得到一个以上的变化了的值,应该如果想从函数得到一个以上的变化了的值,应该用指针变量。用指针变量作函数参数,使指针变用指针变量。用指针变量作函数参数,使指针变量所指向的变量的值发生变化。此时变量的值改量所指向的变量的值发

33、生变化。此时变量的值改变了,主调函数中可以利用这些已改变的值。如:变了,主调函数中可以利用这些已改变的值。如:main()int a,b,*p1,*p2;a=3;b=4;p1=&a;p2=&b;swap(p1,p2);printf(%d,%dn,a,b);/a和和b的值已对换的值已对换/swap(int*pt1,int*pt2)int t;t=*pt1;*pt1=*pt2;*pt2=t;(24)函数的实参和形参类型不一致。函数的实参和形参类型不一致。main()int a=3,b=4;c=fun(a,b);fun(float x,float y)实参实参a、b为整型,形参为整型,形参x、y为实

34、型。为实型。a和和b的值传的值传递给递给x和和y时,时,x和和y的值并非的值并非3和和4。C要求实参与形要求实参与形参的类型一致。如果在参的类型一致。如果在main函数中对函数中对fun作原型作原型声明:声明:fun(float,float);程序可以正常运行,此时,按不同类型间的赋值程序可以正常运行,此时,按不同类型间的赋值的规则处理,在虚实结合后的规则处理,在虚实结合后x=3.0,y=4.0。也可以。也可以将将fun函数的位置调到函数的位置调到main函数之前,也可获正确函数之前,也可获正确结果。结果。(25)不同类型的指针混用。不同类型的指针混用。main()int i=3,*p1;fl

35、oat a=15,*p2;p1=&i;p2=&a;p2=p1;printf(%d,%dn,*p1,*p2);企图使企图使p2也指向也指向i,但,但p2是指向实型变量的指针,是指向实型变量的指针,不能指向整型变量。指向不同类型的指针间的赋不能指向整型变量。指向不同类型的指针间的赋值必须进行强制类型转换。如:值必须进行强制类型转换。如:p2=(float*)p1;作用是先将作用是先将p1的值转换成指向实型的指针,然后的值转换成指向实型的指针,然后再赋给再赋给p2。这种情况在这种情况在C程序中是常见的。例如,用程序中是常见的。例如,用malloc函函数开辟内存单元,函数返回的是指向被分配内存数开辟内

36、存单元,函数返回的是指向被分配内存空间的空间的void*类型的指针。而人们希望开辟的是类型的指针。而人们希望开辟的是存放一个结构体变量值的存储单元,要求得到指存放一个结构体变量值的存储单元,要求得到指向该结构体变量的指针,可以进行如下的类型转向该结构体变量的指针,可以进行如下的类型转换。换。struct studentint num;char name20;float score;struct studentstudent1,*p;p=(struct student*)malloc(LEN);p是指向是指向struct student结构体类型数据的指针,将结构体类型数据的指针,将malloc

37、函数返回的函数返回的void*类型指针转换成指向类型指针转换成指向struct student类型变量的指针。类型变量的指针。(26)没有注意函数参数的求值顺序。例如有以下没有注意函数参数的求值顺序。例如有以下语句:语句:i=3;printf(%d,%d,%dn,i,+i,+i);许许多人认为输出必然是多人认为输出必然是 3,4,5 实际不尽然。在实际不尽然。在Turbo C和其他一些和其他一些C系统中输出系统中输出是是 5,5,4 因为这些系统是采取自右至左的顺序求函数参数的因为这些系统是采取自右至左的顺序求函数参数的值。先求出最右面一个参数值。先求出最右面一个参数(+i)的值为的值为4,再

38、求出,再求出第第2个参数个参数(+i)的值为的值为5,最后求出最左面的参数,最后求出最左面的参数(i)的值的值5。C标准没有具体规定函数参数求值的顺序是自左而标准没有具体规定函数参数求值的顺序是自左而右还是自右而左。但每个右还是自右而左。但每个C编译程序都有自己的顺编译程序都有自己的顺序,在有些情况下,从左到右求解和从右到左求解序,在有些情况下,从左到右求解和从右到左求解的结果是相同的。例如的结果是相同的。例如 fun1(a+b,b+c,c+a);fun1是一个函数名。是一个函数名。3个实参表达式个实参表达式a+b、b+c、c+a。在一般情况下,自左至右地求这在一般情况下,自左至右地求这3个表

39、达式的值和自个表达式的值和自右至左地求它们的值是一样的,但在前面举的例子右至左地求它们的值是一样的,但在前面举的例子是不相同的。因此,建议最好不用会引起二义性是不相同的。因此,建议最好不用会引起二义性的用法。如果在上例中,希望输出的用法。如果在上例中,希望输出“3,4,5”时,时,可以改用可以改用i=3;j=i+1;k=j+1;printf(%d,%d,%dn,i,j,k);(27)混淆数组名与指针变量的区别。混淆数组名与指针变量的区别。main()int i,a5;for(i=0;i5;i+)scanf(%d,a+);企图通过企图通过a的改变使指针下移,每次指向欲输入数的改变使指针下移,每次

40、指向欲输入数据的数组元素。它的错误在于不了解数组名代表据的数组元素。它的错误在于不了解数组名代表数组首地址,它的值是不能改变的,用数组首地址,它的值是不能改变的,用a+是错误是错误的,应当用指针变量来指向各数组元素。即:的,应当用指针变量来指向各数组元素。即:int i,a5,*p;p=a;for(i=0;i5;i+)scanf(%d,p+);或或int a5,*p;for(p=a;pa+5;p+)scanf(%d,p);(28)混淆结构体类型与结构体变量的区别,对一混淆结构体类型与结构体变量的区别,对一个结构体类型赋值。个结构体类型赋值。struct workerlong int num;c

41、har name20;char sex;int age;workernum=187045;strcpy(workername,ZhangFun);workersex=M;workerage=18;这是错误的,只能对变量赋值而不能对类型赋值。这是错误的,只能对变量赋值而不能对类型赋值。上面只定义了上面只定义了struct worker类型而未定义变量。类型而未定义变量。应改为应改为struct workerlong int num;char name20;char sex;int age;struct worker worker-1;worker-1num=187045;strcpy(worke

42、r-1name,Zhang Fun);worker-1sex=M;worker-1age=18;今定义了结构体变量今定义了结构体变量worker-1,并对其中的各成员,并对其中的各成员赋值。赋值。(29)使用文件时忘记打开,或打开方式与使用情况使用文件时忘记打开,或打开方式与使用情况不匹配。不匹配。例如,对文件的读写,用只读方式打开,却企图向例如,对文件的读写,用只读方式打开,却企图向该文件输出数据,例如:该文件输出数据,例如:if(fp=fopen(test,r)=NULL)printf(cannot open this filen);exit(0);ch=fgetc(fp);while(c

43、h!=#)ch=ch+4;fputc(ch,fp);ch=fget(fp);对以对以“r”方式方式(只读方式只读方式)打开的文件,进行既读又打开的文件,进行既读又写的操作,显然是不行的。写的操作,显然是不行的。此外,有的程序常忘记关闭文件,虽然系统会自此外,有的程序常忘记关闭文件,虽然系统会自动关闭所用文件,但可能会丢失数据。因此必须动关闭所用文件,但可能会丢失数据。因此必须在用完文件后关闭它。在用完文件后关闭它。以上只是列举了一些初学者常出现的错误,这些以上只是列举了一些初学者常出现的错误,这些错误大多是对于错误大多是对于C语法不熟悉之故。对语法不熟悉之故。对C语言使用语言使用多了,比较熟练

44、了,犯这些错误自然就会减少了。多了,比较熟练了,犯这些错误自然就会减少了。在深入使用在深入使用C语言后,还会出现其他一些更深入、语言后,还会出现其他一些更深入、更隐蔽的错误。更隐蔽的错误。程序出错有三种情况:程序出错有三种情况:语法错误。指违背了语法错误。指违背了C语法的规定,对这类错语法的规定,对这类错误,编译程序一般能给出误,编译程序一般能给出“出错信息出错信息”,并且告,并且告诉你在哪一行出错。只要细心,是可以很快发现诉你在哪一行出错。只要细心,是可以很快发现并排除的。并排除的。逻辑错误。程序并无违背语法规则,但程序执逻辑错误。程序并无违背语法规则,但程序执行结果与原意不符。这是由于程序

45、设计人员设计行结果与原意不符。这是由于程序设计人员设计的算法有错或编写程序有错,通知给系统的指令的算法有错或编写程序有错,通知给系统的指令与解题的原意不相同,即出现了逻辑上的混乱。与解题的原意不相同,即出现了逻辑上的混乱。例如:前面第例如:前面第9条错误:条错误:sum=0;i=1;while(i=100)sum=sum+i;i+;语法并无错误。但语法并无错误。但while语句通知给系统的信息是语句通知给系统的信息是当当i100时,执行时,执行“sum=sum+i;”。C系统无法辨系统无法辨别程序中这个语句是否符合作者的原意,而只能别程序中这个语句是否符合作者的原意,而只能忠实地执行这一指令。

46、这种错误比语法错误更难忠实地执行这一指令。这种错误比语法错误更难检查。要求程序员有较丰富的经验。检查。要求程序员有较丰富的经验。运行错误。程序既无语法错误,也无逻辑错误,运行错误。程序既无语法错误,也无逻辑错误,但在运行时出现错误甚至停止运行。例如:但在运行时出现错误甚至停止运行。例如:int a,b,c;scanf(%d%d,&a,&b);c=b/a;printf(c=%dn,c);输入输入a和和b的值,的值,输出输出b/a的值,的值,程序没有错。程序没有错。但但是如果输入是如果输入a的值为的值为0,就会出现错误。就会出现错误。因此程序因此程序应能适应不同的数据,应能适应不同的数据,或者说能

47、经受各种数据的或者说能经受各种数据的“考验考验”,具有具有“健壮性健壮性”。写完一个程序只能说完成任务的一半写完一个程序只能说完成任务的一半(甚至不到一甚至不到一半半)。调试程序往往比写程序更难,更需要精力、。调试程序往往比写程序更难,更需要精力、时间和经验。常常有这样的情况:程序花一天就时间和经验。常常有这样的情况:程序花一天就写完了,而调试程序二三天也未能完。有时一个写完了,而调试程序二三天也未能完。有时一个小小的程序会出错五六处,而发现和排除一个错小小的程序会出错五六处,而发现和排除一个错误,有时竟需要半天,甚至更多。希望读者通过误,有时竟需要半天,甚至更多。希望读者通过实践掌握调试程序

48、的方法和技术。实践掌握调试程序的方法和技术。16.2程程 序序 调调 试试 所谓程序调试是指对程序的查错和排错。所谓程序调试是指对程序的查错和排错。调试程序一般应经过以下几个步骤:调试程序一般应经过以下几个步骤:(1)先进行人工检查,即静态检查。在写好一个程序先进行人工检查,即静态检查。在写好一个程序以后,不要匆匆忙忙上机,而应对纸面上的程序进以后,不要匆匆忙忙上机,而应对纸面上的程序进行人工检查。这一步是十分重要的,它能发现程序行人工检查。这一步是十分重要的,它能发现程序设计人员由于疏忽而造成的多数错误。而这一步骤设计人员由于疏忽而造成的多数错误。而这一步骤往往容易被人忽视。有人总希望把一切

49、推给计算机往往容易被人忽视。有人总希望把一切推给计算机系统去做,但这样就会多占用机器时间。而且,作系统去做,但这样就会多占用机器时间。而且,作为一个程序人员应当养成严谨的科学作风,每一步为一个程序人员应当养成严谨的科学作风,每一步都要严格把关,不把问题留给后面的工序。为都要严格把关,不把问题留给后面的工序。为了更有效地进行人工检查,所编的程序应注意力了更有效地进行人工检查,所编的程序应注意力求做到以下几点:求做到以下几点:应当采用结构化程序方法编应当采用结构化程序方法编程,以增加可读性程,以增加可读性;尽可能多加注释,以帮助理尽可能多加注释,以帮助理解每段程序的作用解每段程序的作用;在编写复杂

50、的程序时,不要在编写复杂的程序时,不要将全部语句都写在将全部语句都写在main函数中,而要多利用函数,函数中,而要多利用函数,用一个函数来实现一个单独的功能。这样既易于阅用一个函数来实现一个单独的功能。这样既易于阅读也便于调试,各函数之间除用参数传递数据这一读也便于调试,各函数之间除用参数传递数据这一渠道以外,数据间尽量少出现耦合关系,便于分别渠道以外,数据间尽量少出现耦合关系,便于分别检查和处理。检查和处理。(2)在人工在人工(静态静态)检查无误后,才可以上机调试。通检查无误后,才可以上机调试。通过上机发现错误称动态检查。在编译时给出语法错过上机发现错误称动态检查。在编译时给出语法错误的信息

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

当前位置:首页 > 办公、行业 > 各类PPT课件(模板)
版权提示 | 免责声明

1,本文(常见错误和程序调试课件.ppt)为本站会员(晟晟文业)主动上传,163文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。
2,用户下载本文档,所消耗的文币(积分)将全额增加到上传者的账号。
3, 若此文所含内容侵犯了您的版权或隐私,请立即通知163文库(发送邮件至3464097650@qq.com或直接QQ联系客服),我们立即给予删除!


侵权处理QQ:3464097650--上传资料QQ:3464097650

【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。


163文库-Www.163Wenku.Com |网站地图|