? ? ? ?"OutOfRange" ? ? ? ? ?: \
17 ? ? ? "Unknown")))))
复制代码
2.2 全局状态标志(errno)
? ? ?Unix系统调用或某些C标准库函数出错时,通常返回一个负值,并设置全局整型变量errno为一个含有错误信息的值。例如,open函数出错时返回-1,并设置errno为EACESS(权限不足)等值。
?
? ? ?C标准库头文件中定义errno及其可能的非零常量取值(以字符'E'开头)。在ANSI C中已定义一些基本的errno常量,操作系统也会扩展一部分(但其对错误描述仍显匮乏)。Linux系统中,出错常量在errno(3)手册页中列出,可通过man 3 errno命令查看。除EAGAIN和EWOULDBLOCK取值相同外,POSIX.1指定的所有出错编号取值均不同。
?
? ? ?Posix和ISO C将errno定义为一个可修改的整型左值(lvalue),可以是包含出错编号的一个整数,或是一个返回出错编号指针的函数。以前使用的定义为:
?
1 extern int errno;
? ? ?但在多线程环境中,多个线程共享进程地址空间,每个线程都有属于自己的局部errno(thread-local)以避免一个线程干扰另一个线程。例如,Linux支持多线程存取errno,将其定义为:
?
1 extern int *__errno_location(void);
2 #define errno (*__errno_location())
? ? ?函数__errno_location在不同的库版本下有不同的定义,在单线程版本中,直接返回全局变量errno的地址;而在多线程版本中,不同线程调用__errno_location返回的地址则各不相同。
?
? ? ?C运行库中主要在math.h(数学运算)和stdio.h(I/O操作)头文件声明的函数中使用errno。
?
? ? ?使用errno时应注意以下几点:
?
? ? ?1) 函数返回成功时,允许其修改errno。
?
? ? ?例如,调用fopen函数新建文件时,内部可能会调用其他库函数检测是否存在同名文件。而用于检测文件的库函数在文件不存在时,可能会失败并设置errno。这样, fopen函数每次新建一个事先并不存在的文件时,即使没有任何程序错误发生(fopen本身成功返回),errno也仍然可能被设置。
?
? ? ?因此,调用库函数时应先检测作为错误指示的返回值。仅当函数返回值指明出错时,才检查errno值:
?
1 //调用库函数
2 if(返回错误值)
3 ? ? //检查errno
? ? ?2) 库函数返回失败时,不一定会设置errno,取决于具体的库函数。
?
? ? ?3) errno在程序开始时设置为0,任何库函数都不会将errno再次清零。
?
? ? ?因此,在调用可能设置errno的运行库函数之前,最好先将errno设置为0。调用失败后再检查errno的值。
?
? ? ?4) 使用errno前,应避免调用其他可能设置errno的库函数。如:
?
1 if (somecall() == -1)
2 {
3 ? ? printf("somecall() failed\n");
4 ? ? if(errno == ...) { ... }
5 }
? ? ?somecall()函数出错返回时设置errno。但当检查errno时,其值可能已被printf()函数改变。若要正确使用somecall()函数设置的errno,必须在调用printf()函数前保存其值:
?
1 if (somecall() == -1)
2 {
3 ? ? int dwErrSaved = errno;
4 ? ? printf("somecall() failed\n");
5 ? ? if(dwErrSaved == ...) { ... }
6 }
? ? ?5) 使用现代版本的C库时,应包含使用头文件;在非常老的Unix 系统中,可能没有该头文件,此时可手工声明errno(如extern int errno)。
?
? ? ?C标准定义strerror和perror两个函数,以帮助打印错误信息。
?
#include
?
char *strerror(int errnum);
?
? ? ?该函数将errnum(即errno值)映射为一个出错信息字符串,并返回指向该字符串的指针。可将出错字符串和其它信息组合输出到用户界面,或保存到日志文件中,如通过fprintf(fp, "somecall failed(%s)", strerror(errno))将错误消息打印到fp指向的文件中。
?
? ? ?perror函数将当前errno对应的错误消息的字符串输出到标准错误(即stderr或2)上。
?
#include
?
void perror(const char *msg);
?
? ? ?该函数首先输出由msg指向的字符串(用户自己定义的信息),后面紧跟一个冒号和空格,然后是当前errno值对应的错误类型描述,最后是一个换行符。未使用重定向时,该函数输出到控制台上;若将标准错误输出重定向到/dev/null,则看不到任何输出。
?
? ? ?注意,perror()函数中errno对应的错误消息集合与strerror()相同。但后者可提供更多定位信息和输出方式。
?
? ? ?两个函数的用法示例如下:
?
复制代码
?1 int main(int argc, char** argv)
?2 {
?3 ? ? errno = 0;
?4 ? ? FILE *pFile = fopen(argv[1], "r");
?5 ? ? if(NULL == pFile)
?6 ? ? {
?7 ? ? ? ? printf("Cannot open file '%s'(%s)!\n", argv[1], strerror(errno));
?8 ? ? ? ? perror("Open file failed");
?9 ? ? }
10 ? ? else
11 ? ? {
12 ? ? ? ? printf("Open file '%s'(%s)!\n", argv[1], strerror(errno));
13 ? ? ? ? perror("Open file");
14 ? ? ? ? fclose(pFile);
15 ? ? }
16 ?
17 ? ? return 0;
18 }
复制代码
? ? ?执行结果为:
?
复制代码
?1 [wangxiaoyuan_@localhost test1]$ ./GlbErr /sdb1/wangxiaoyuan/linux_test/test1/test.c
?2 Open file '/sdb1/wangxiaoyuan/linux_test/test1/test.c'(Success)!
?3 Open file: Success
?4 [wangxiaoyuan_@localhost test1]$ ./GlbErr NonexistentFile.h
?5 Cannot open