•要真正学好C、用好C并不容易,“灵活”固然是好事,但也使人难以掌握,尤其是初学者往往出了错还不知怎么回事。C编译程序对语法的检查不如其他高级语言那样严格(这是为了给程序人员留下“灵活”的余地)。因此,往往要由程序设计者自己设法保证程序的正确性。调试一个C程序要比调试一个PASCAL或FORTRAN程序更困难一些。需要不断积累经验,提高程序设计和调试程序的水平。•下面是常见的一些错误:第11章常见错误分析(1)忘记定义变量。如:main(){x=3;y=6;printf(%d\n,x+y);}•C要求对程序中用到的每一个变量都必须定义其类型,上面程序中没有对x、y进行定义。应在函数体的开头加intx,y;这是学过BASIC和FORTRAN语言的读者写C程序时常见的一个错误。在BASIC语言中,可以不必先定义变量类型就可直接使用。在FORTRAN中,未经定义类型的变量按隐含的I-N规则决定其类型,而C语言则要求对用到的每一个变量都要在本函数中定义(除非已定义为外部变量)。(2)输入输出的数据的类型与所用格式说明符不一致。例如,若a已定义为整型,b已定义为实型。a=3;b=4.5;printf(%f%d\n,a,b);编译时不给出出错信息,但运行结果将与原意不符,输出为0.00000016402它们并不是按照赋值的规则进行转换(如把4.5转换成4),而是将数据在存储单元中的形式按格式符的要求组织输出(如b占4个字节,只把最后两个字节中的数据按%d,作为整数输出)。(3)未注意int型数据的数值范围。在TurboC编译系统,对一个int整型数据分配两个字节。因此一个整数的范围为-215~215-1,即-32768~32767。常见这样的程序段:intnum;num=89101;printf(%d,num);•得到的却是23565,原因是89101已超过32767。两个字节容纳不下89101,则将高位截去。见图16.1。即将超过低16位的数截去。即将89101减去216(即16位二进制所形成的模)。89101-65536=23565。有时还会出现负数。例如num=196607;输出得-1。因为196607的二进制形式为00000000000000101111111111111111去掉高位10,低16位的值是-1(-1的补码是:1111111111111111)。对于超过整个范围的数,要用long型,即改为longintnum;num=89101;printf(%ld,num);请注意,如果只定义num为long型,而在输出时仍用“%d”说明符,仍会出现以上错误。(4)输入变量时忘记使用地址符。如:scanf(%d%d,a,b);这是许多初学者刚学习C语言时一个常见的疏忽,或者说是习惯性的错误,因为在其他语言中在输入时只需写出变量名即可,而C语言要求指明“向哪个地址标识的单元送值”。应写成scanf(%d%d,&a,&b);(5)输入时数据的形式与要求不符。用scanf函数输入数据,应注意如何组织输入数据。假如有以下scanf函数:scanf(%d%d,&a,&b);有人按下面的方法输入数据:3,4这是错的。数据间应该用空格来分隔。应该用以下方法输入:34(中间有空格)如果scanf函数为scanf(%d,%d,&a,&b);对scanf函数中格式字符串中除了格式说明符外,对其他字符必须按原样输入。因此,应按以下方法输入:3,4此时如果用“34”反而错了。(6)在用scanf函数向字符数组输入数据时,在数组名前面多加了&,例如:chara[20];scanf(“%s”,&a);在对变量输入数据时应当加&,例如&a,但是数组名本身已经表示地址,不需要再加&,应当写成:scanf(“%s”,a);(7)在用scanf函数向数值型数组输入数据时,直接用数组名,例如:inta[20];scanf(“%d”,a);这是错误的。数值型的数组不能一次性整体输入,只能一个个元素单独输入,应当写成:inta[20];inti;for(i=0;i20;i++)scanf(“%d”,&a[i]);(8)语句后面漏分号,例如:a=3b=4;(9)在预处理指令后面加分号,例如:#includestdio.h;注意预处理不是语句,是指令,所以不需要分号。(10)在不该加分号的地方加分号,例如:if(ab);printf(“aislargerthanb\n”);这个写法相当于if的条件成立时,执行空语句(就是那个分号);而无论条件是否成立,printf都需要执行。显然这不是程序的本意。再例如:inta[20];inti;for(i=0;i20;i++);scanf(“%d”,&a[i]);for后面的那个分号,直接导致scanf不属于循环体。(11)对于应该有花括号的复合语句,忘记加花括号,例如:sum=0;i=1;while(i100)sum=sum+i;i++;这个写法相当于while的循环体只有一句sum=sum+i;尽管i++的格式做了缩进处理,但是i++依然不属于while循环,所以导致while成了死循环。(12)括号不配对,例如:while((c=getchar()!=‘#’)putchar(c);(13)在用标识符时,混淆了大写字母和小写字母的区别,例如:intA,B,C;a=1;b=2;c=3;这两个错误编译时都会报错,所以还算是比较容易查出错误。(14)误把“=”作为“等于”运算符。在许多高级语言中,用“=”符号作为关系运算符“等于”,但是C语言偏偏不是。例如,if(score=100)n++;C编译系统将(score=100)作为赋值表达式处理,将100的值赋给score,然后判断score的值是否零,若为非零,则作为“真”;若为零作为假。显然100永远不会是false,所以n++总是会执行。(15)引用数组元素时误用了圆括弧。如:intmain(){inti,a(10);for(i=0;i<10;i++)scanf(%d,&a(i));return0;}C语言中对数组的定义或引用数组元素时必须用方括弧。(16)在定义数组时,将定义的“元素个数”误认为是“可使用的最大下标值”。intmain(){inta[10]={1,2,3,4,5,6,7,8,9,10};inti;for(i=1;i<=10;i++)printf(%d,a[i]);return0;}想输出a[1]到a[10]。但一些初学者常犯的错误。C语言规定定义时用a[10],表示a数组有10个元素,而不是可以用的最大下标值为10。数组只包括a[0]到a[9]10个元素,因此用a[10]就超出a数组的范围了。(17)对二维或多维数组的定义和引用的方法不对。intmain(){inta[5,4];…printf(%d,a[1+2,2+2]);…}对二维数组和多维数组在定义和引用时必须将每一维的数据分别用方括弧括起来。上面a[5,4]应改为a[5][4],a[1+2,2+2]应改为a[1+2][2+2]。根据C的语法规则,在一个方括弧中的是一个维的下标表达式,a[1+2,2+2]中方括弧中的“1+2,2+2”是一个逗号表达式,它的值是第二个数值表达式的值,即2+2的值为4。所以a[1+2,2+2]相当于a[4]。而a[4]是a数组的第4行的首地址。因此执行printf函数输出的结果并不是a[3][4]的值,而是a数组第4行的首地址。(18)误以为数组名代表数组中全部元素。例如:intmain(){inta[4]={1,3,5,7};printf(%d%d%d%d\n,a);return0;}企图用数组名代表全部元素。在C语言中,数组名代表数组首地址,不能通过数组名输出4个整数。(19)混淆字符数组与字符指针的区别。intmain(){charstr[4];str=Computerandc;printf(%s\n,str);return0;}编译出错。str是数组名,代表数组首地址。在编译时对str数组分配了一段内存单元,因此在程序运行期间str是一个常量,不能再被赋值。因此,str=“Computerandc”是错误的。如果把“charstr[4];”改成“char*str;”,则程序正确。此时str是指向字符数据的指针变量,str=“Computerandc”是合法的,它将字符串的首地址赋给指针变量str,然后在printf函数语句中输出字符串“Computerandc”。因此应当弄清楚字符数组与字符指针变量用法的区别。(20)在引用指针变量之前没有对它赋予确定的值。intmain(){char*p;scanf(%s,p);…}没有给指针变量p赋值就引用它,编译时给出警告信息。应当改为char*p,c[20];p=c;scanf(%s,p);即先根据需要定义一个大小合适的字符数组c,然后将c数组的首地址赋给指针变量p,此时p有确定的值,指向数组c。再执行scanf函数就没有问题了,把从键盘输入的字符串存放到字符数组c中。(21)switch语句的各分支中漏写break语句。例如:switch(score){case5:printf(Verygood!);case4:printf(Good!);case3:printf(Pass!);case2:printf(Fail!);defult:printf(dataerror!);}上述switch语句的作用是希望根据score(成绩)打印出评语。但当score的值为5时,输出为VeryGood!Good!Pass!Fail!dataerror!原因是漏写了break语句。case只起标号的作用,而不起判断作用,因此在执行完第一个printf函数语句后接着执行第2、3、4、5个printf函数语句。应改为switch(score){case5:printf(Verygood!);break;case4:printf(Good!);break;case3:printf(Pass!);break;case2:print(Fail!);break;defult:print(dataerror!);}(22)混淆字符和字符串的表示形式。charsex;sex=M;…sex是字符变量,只能存放一个字符。而字符常量的形式是用单引号括起来的,应改为sex='M';“M”是用双引号括起来的字符串,它包括两个字符:‘M’和‘\0’,无法存放到字符变量sex中。(23)使用自加(++)和自减(--)运算符时出的错误。例如:intmain(){intp,a[5]={1,3,5,7,9};p=a;printf(%d,*p++);return0;}不少人认为“*p++”的作用是先使p加1,即指向第1个元素a[1]处,然后输出第一个元素a[1]的值3。其实应该是先执行p++,而p++的作用是先用p的原值,用完后使p加1。p的原值指向数组a的第0个元素a[0],因此*p就是第0个元素a[0]的值1。结论是先输出a[0]的值,然后再使p加1。如果是*(++p),则先使p指向a[1],然后输出a[1]的值。(24)忘记对所调用的函数进行函数原型声明。如:intmain(){floata,b,c;a=3.5;b=-7.6;c=max(a,b);printf(“%f”,c);return0;}floatmax(floatx,floaty){returnxy?x:y;}改法1,在main中增加max函数声明:intmain(){floatmax(floatx,floaty);floata,b,c;a=3.5;b=-7.6;c=max(a,b);printf(“%f”,c);return0;}改法2,将max函数定义提到main之前,如:floatmax(floatx,floaty){returnxy?x:y;}intmain(){floata,b,c;a=3.5;b=-7.6;c=max(a,b);printf(“%f”,c);return0;}(25