14.8 错误处理:errno.h
14.8.1 errno:一个左值
任何程序都可能出错。如果一旦遇到错误,程序立即崩溃或立即罢工是不行的,这样的程序比温室的花朵还要脆弱,不满足商业程序最基本的要求。
健壮的程序要求能够对出现的错误进行适当的处理,至少也要让用户的数据不至于受到损失。
“errno.h”提供了一种老式的错误处理方法。这种方法的原理是:当在调用某个函数时,如果在函数内部发生错误,函数会假装什么事儿也没发生似的返回一个值(这个值很可能是错误的),但同时把某个“外部变量”的值改成一个特殊的值。这个函数的上一级通过检查这个“外部变量”的值是否被改变为一个特殊值来判断是否发生过错误。
“errno.h”中定义的“外部变量”就是“errno”这是一个被保留的标识符,不可以用于其他用途。
“errno”有可能是外部变量,也有可能只是一个类似对象的宏名。当它是宏名时,其宏体必须是一个左值表达式,否则是没有办法把出错的信息写入的。Dev C++的“errno.h”中对它的定义如下所示。
可以看到,“extern int errno;”是一个外部变量的声明,其中的“errno”是一个外部变量;而在“#define errno(_errno())”命令中,“errno”则是一个返回“int ”值的函数。
14.8.2 errno的值
C标准定义了3个宏表示errno的值,如下所示。
EDOM:表示函数定义域错误,比如对负数开平方。
ERANGE:表示函数返回值值域错误,比如求10的1000次方。
EILSEQ:翻译多字节字符序列时遇到编码错误。这个宏是在95年C标准修订时增补的。
下面代码演示了errno在出错时值的变化情况。
程序代码14-6
其中,把“errno”的值重新设置为0是必要的,否则即使不再发生错误,这个值也不会自己重新变为0。
但是必须要说的是,“printf("%s错误\n", errno=EDOM?"定义域":"值域");”用得很不专业。这个调用至少应该写成:
fprintf(stderr,"%s错误\n",errno=EDOM?"定义域":"值域");
因为“stderr”是非缓冲的,可以立即输出。
更专业的写法应该使用perror()函数,这个函数的原型在“stdio.h”中。把前面代码中的“printf("%s错误\n",errno=EDOM?"值域":"定义域");”换为“perror(errno=EDOM?"定义域":"值域");”之后,程序的输出如图14-6所示。
图14-6 eerno的值
可以看到perror()函数给出了更多的出错信息。