在前面 15 节的介绍中,我们写了不少 linux 中的C语言程序。相信大家也发现了,即使再简单的C语言程序,也是有可能写错的,毕竟人不是机器。
linux 中的程序错误
写C语言程序出的错千奇百怪,有的是手误写错了符号,有的是头脑发热写反了逻辑,甚至有时还会写错数字。语法错误,编译器能够直接指出,对照着修正没有什么难度。逻辑出错的程序才叫人头疼,这类程序能够编译通过,却得不到正确结果,因为没有编译器帮忙,只能由程序员自己去找错误修正。
程序逻辑出错大体可以分为两种:
- 一是出错时,程序不会退出,可以继续往下执行。例如调用 open 函数打开文件失败,程序不会退出,还能执行程序员设定的错误处理语句,例如打印错误信息等。
- 一是一旦出错,程序就直接崩溃退出。比如非法操作内存引起的段错误(这种错误在大型项目开发中经常出现),再例如把 0 当作被除数的浮点错误等。
第一种错误可以勉强认为是预料之中的错误。以 open 函数为例,在定义 open 函数时就已经考虑可能会出现的各种情况了:大多数情况下,open 函数能够成功打开文件。但是在权限不足,或者打开方式不正确时,的确会出现 open 函数打开文件失败的情况。
程序员在定义 open 函数时,会尽力将所有可能的情况都纳入考虑,最后看起来好像 open 函数本身就具有处理错误的能力,输入 man 命令查询其手册:
得知 open 函数在成功打开文件时,会返回文件描述符。如果打开文件出错,它就会返回 -1,并且可以根据 errno 查询出错原因。不管 open 函数执行成功还是出错,所有的情况都在考虑中。
不管成功还是出错,open 函数都告诉计算机该如何处理,所以即使出错,程序也不会意外退出。
第二种错误则完全出乎意料之外了。程序员之前没有考虑到这种情况,所以没有告诉计算机该如何处理,死板的计算机自己又懒得思考,所以出现这种错误时,直接草草结束掉程序了事。
例如,程序员不小心让 0 做被除数了:
#include <stdio.h>
int main()
{
int a = 8/0;
printf("%d\n", a);
return 0;
}
这时程序处理不了 8/0 的计算,只好罢工,直接退出:
设计程序要考虑全面
第二种错误的症结往往很隐蔽,要从几万行甚至几十万行代码中找出错误的原因,难度可想而知。所以,程序员在编写代码时,要将所有可能会发生的事情都考虑到,否则就可能会出现第二种错误。
例如下面这个问题:
编写 C语言程序,取 100 个最大值为 10 的随机数 num,打印出 100/num 的值。
这个问题是简单的,很快就能写出程序:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char i, j = 100;
while(j--){
i = rand()%10;
printf("num %d: %d\n", 100-j, 100/i);
}
return 0;
}
编译执行,发现程序并没能按照预期的效果运行:
程序只打印了 12 个数就退出了,并且提示“Floating point exception”,这就是因为没有考虑随机值 i 可能等于 0。因为 i 的值是随机的,程序员并不能准确的得到程序出错的时机,如果这个错误隐藏在几十万行代码里,想像一下这个错误的可怕吧。
所以,程序员一定要足够细心,把所有可能的情况都要考虑到。以上代码可以改写为:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char i, j = 100;
while(j--){
i = rand()%10;
if(i)
printf("num %d: %d\n", 100-j, 100/i);
else
printf("num %d: inf\n", 100-j);
}
return 0;
}
只有当 i 不为 0 的时候才计算 100/i,i等于0的时候,打印出 inf。这下,程序终于可以正常工作了:
看来,程序员都该是心思缜密的人。