C语言异常处理+纠错与调试
功能强大、使用便捷灵活是C语言最大的特点。但在编译时对语法检查并不如其他高级语言那么严格,这就给编程人员留下“犯错的机会”,同样还是由于这个“灵活”给程序的调试带来了诸多不便,特别是对初学C语言的人来说,经常会出一些连自己都不知道错在哪里的错误。面对有错的程序,更不知该如何改起。
所谓程序异常(Exception),就是程序在运行过程中,由于使用环境的变化及用户的操作而产生的错误。
所有的程序都有bug,通常程序规模越大,bug也就更容易产生。为了尽可能地减少正式发布软件中的bug,一个软件在经历了Demo版本后,还会经历Alpha版本(内部测试版),然后是Beta版本(外部测试版),最后才发布Release版本,也就是正式版本,之后还会发布补丁包和升级版本,这就说明了程序错误是不可避免的。
语法错误是指代码不符合C语言的语句。即程序中执行了错误的语句或者函数类等,致使编译程序无法进行,这里我们假设没有编辑上的错误,也就是没有关键字或者变量名写错。编译系统能查出此类错误并报告错误的原因,有语法错误的代码不能通过编译,改正后才能通过编译。这是C语言初学者出现最多的错误,比如,分号“;”是每个C语句的结束的标志,在C语句后忘记写“;”就是语法错误,发生语法错误的程序,编译通不过,这种错误只要编写程序时细心认真,一般是可以解决的。
逻辑错误也称语义错误就是程序没有实现编程人员的设计意图和功能。程序虽然可以正常运行,但得不到所预定的程序结果(或正确的结果)。也就是说由于程序设计者对程序逻辑规划错误,程序没有按照程序设计者设定的思路来执行,逻辑错误一般是因算法考虑不周引起的,也有些是因为编程时疏忽大家,如将“+”号写成“*”号或将“=” 赋值符等同“==”比较符等。
C语言提供动态内存机制为灵活地进行程序设计提供了便捷,但同时也提高了内存错误发生的机会。下面精选一些C语言中常见内存错误类型,
类型1:内存未分配成功却被调用。
类型2:引用了尚未初始化的指针。
类型3:越界操作内存。
类型4:未释放内存,形成内存泄漏。
类型5:内存已被释放了却继续使用它。
程序纠错的助手就是编译器,是一种程序开发环境,它支持应用程序代码的单步执行和查看代码中变量的内容。通常有源代码级调试和二进制调试之分。
几乎所有的开发环境都是比较强大的编译器。运行编译器,就可以加载打开的源代码,然后在调试器中运行。可以查看每个变量的当前值和使用状态,查看数据结构、类成员的数据和指针的值等丰富的信息;还可以设置断点,检查内存,执行单行代码的调试,或者跳跃性地跟踪代码的调用。
程序异常处理因遵循以下几个方面:
(1) 把可能出现异常的代码和异常处理代码隔离开,结构会更清晰。
(2) 把内层错误的处理直接上传到指定的外层来处理,可使处理流程快速简洁。一般的处理方法是通过一层一层的返回错误指令,逐层上传到指定层,但层数过多时就需要进行非常多的判断,代码复杂,要想考虑周全就更加困难。
(3) 在出现异常时,能够获取异常的信息并指出,以友好的方式传递给用户。
这样做不仅可以使程序更加安全、健壮,而且一旦程序出现了问题也更容易查到原因,修改时做到有的放矢。
C语言常见典型错误解析
缺少分号或分号位置错误
路径使用的错误
误把“=”作为“等于”比较符用
遗漏花括号
括号配对错误
大小写字母的区别
忘记定义变量
错误使用指针
循环语句中忘记中断语句
混淆字符和字符串的表示形式
程序自加(++)和自减(一一)运用错误
数组及数组下标
int型数据的数值范围
函数的错误使用
混淆数组名及指针变量区别
混淆结构体类型和结构体变量区别
使用文件时忘记打开文件或打开文件方式不对
C语言缺少分号错误。
a=3
b=4
错误解析:C语言规定语句末尾必须有分号。分号是C语句不可缺少的一部分。这也是和其他语言不同的。编译程序在“a=3”后面未发现分号,就会把下一行“b=4”也作为上一行的语句的一部分,这就出现语法错误。有时编译时指出某行有错,但在该行上并未发现错误,应该检查上一行是否漏了分号。
【例20—2】C语言分号位置错误。
if(a>b);
printf("a is larger than b\n");
错误解析:本意为当a>b时输出“a is larger than b”的信息。但由于在if(a>b)后加了分号,因此if语句到此结束。
路径使用错误。
fp=fopen(″c:\new\tools.″C,″:″); /*错误*/
fp=fopen(″c:\\new\\tools,c″,″r″) /*正确*/
错误解析:反斜杠(\)表示一个DOS的路径。但C语言中表示一个换码字符(例如\n换行,\t制表符),因此一定要表示路径时应用两个反斜杠。
误把“=”作为“等于”比较符用。
if(a=b) puts(″Egual″);else puts(″Not egual″); /*错误*/
if{(a==b) puts(″Egual″);else puts(″Not egual″)} /*正确*/
错误解析:C语言中‵=‵是赋值符(在BASIC或PASCAL程序中是比较符,赋值符用:=表示)。‵==‵是比较符,检查前后两个表达式是否相等。同样要注意千万不要忘记puts(″Egual″)后面的分号(;)。
遗漏花括号。
C语言中对复合语句段需要添加上花括号,不能遗漏。
sum=0;
i=l;
while(i<=100)
sum=sum+i;
i++;
错误解析:程序本意是实现1+2+3+…+100。但上面语句只重复执行sum+1操作,而且陷入无限循环,其原因是i值始终没有变化,错误在于没有写成复合语句形式。因此while语句的范围到其后第一个分号为止。语句“i++;”不属于循环体范围之内。因此必须在while语句中加上花括号。
括号配对错误。
while((c=getchar()!=‵#‵)
putchar(c);
错误解析:当一个语句中使用多层括弧时易犯这类错误,主要是粗心所致。缺少一个右括号。
大小写字母的区别。
main()
{ int a,b,c;
a=2;b=3;
C=A+B;
printf(″%d+%d=%d″,A,B,C);
}
错误解析:编译时出错。编译程序把a和A认作是两个不同的变量名处理,同样b和B,c和C都分别代表两个不同的变量。
忘记定义变量
main ( )
{
x=3;
y=6;
printf("%d\n",x+y);
}
错误解析:C语言中对变量应先定义后使用,程序中用到的每一个变量都必须事先进行定义,指明其数据类型。上面程序中没有对x、y进行定义。应在函数体的开头加“int x,y;”,除非x,y已定义为外部变量。
未为指针分配空间
例如:
main()
{ int *iptr;
*iptr=421;
printf(″*iptr=%d\n″,*iptr);
}
程序缺少中断语句break。
switch (score)
{
case 5:printf("Very good!");
case 4:printf("Good!");
case 3:printf("Pass!");
case 2:print("Fail!");
defult:print("data error!");
}
错误解析:本意switch语句的作用是希望根据score(成绩)打印出评语。由于漏写了break语句,当成绩为5时。将持续执行后续语句,将输出为;Very Good!Good!Pass!Fail!Data error!结果。由于漏写了break语句。case只起标号的作用,而不起判断作用,因此在执行完第一个printf函数语句后接着执行第2、3、4、5个printf函数语句。
程序自加错误。
main()
{
int *p,a[6]={1,3,5,7,9,11};
p=a;
printf(″%d″,*p++);
}
错误解析:不少人认为*p++是使p先加1,即指向a[1],然后输出其中值3,但事实上,执行P++即先执行p原值,再使它+1,因此,输出a[0]为1,然后再进行p加1,如果上式改为x(++p),那么先执行p指向a[l]再输出a[l]值。
scanf("%d%d",a,b);
scanf("%d%d",&a,&b);
注意:不要和变量的引用相混淆。
数组下标是从0开始而不是从1开始。
引用数组元素时误用括号。
对二维或多维数组定义和引用方法不对。
误以为数组名代表数组中全部元素。
int型数据的数值范围。
一般程序对int整型数值分配两个字节,因此一个整数范围为一32768~32767。例如:
int num;
num=89101;
printf(″%d″,num);
错误解析:输出值不是89101其原因是num值已超出int数值范围,为此可以采用long型,即修改程序为:
long int num
num=89101;
printf(″%d″,num); /*不能用%d输出,否则仍出错*/
1. 将函数的形参和函数中的局部变量一起定义。
2. 所调用的函数在调用语句之后才定义,而在调用前未加说明。
3. 误认为形参值的改变会影响实参的值。
4. 函数的实参和形参类型不一致。
5. 不同类型指针的混用。
6. 函数参数的求值顺序。
混淆数组名及指针变量。
main()
{ int i,a[6];
for(i=0;i<6;i++)
scanf(″%d″,a++);
……
}
区分混淆结构体类型和结构体变量。
struct worker
{
int num;
char name[20];
char sex;
int age;
}
worker.num=123;
strcpy(worker.name,″Gong Ming″);
worker.sex=‵M‵;
worker.age=17;
……
错误解析:本例是错误的,不能对类型赋值,只能对变量赋值。
使用文件错误。
if((fp=fopen(″fest″,″r″))==NULL)
{
printf(″cannot open this file\n″);
exif(0);
}
ch=fgetc(fp);
while(ch!=‵#‵)
{
ch=ch+4;
fputc(ch,fp);
ch=fgetc(fp);
}
错误解析:该例以″r″(只读)形式打开文件,进行即读又写操作显然是错误的,同时也遗漏了关闭文件的语句(虽然系统退出程序时会自动关闭所打开文件,但可能会丢失数据)。