一、 errno
包含了头文件errno.h
后就能直接使用该变量。
该变量在头文件中的定义
#ifndef errno
extern int errno;
#endif
该头文件在/usr/include/errno.h
:
该变量用于保存程序的错误码,如果程序执行正确,则该变量不会更新。这些错误码通常是被定义在errno.h中以E开头的宏。 #define EDOM 33
该头文件在/usr/include/asm/errno.h
,在Ubuntu 16.04-64
系统上,该头文件路径是/usr/include/asm-generic/errno.h
如下图:
以及
因为,errno可以把最后一次调用C的错误代码保留,如果最后一次调用C函数成功,则不会更改errno,所以如果你要使用之前,可以先清空errno,然后通过errno判断下面要执行的函数是否成功。
二、 把errno的数字转换成相应的文字说明
1、 使用strerror函数
- 函数原型:
char * strerror(int errno)
- 使用方法:
fprintf(stderr, "%s", strerror(errno));
2、 使用perror函数
- 函数原型:
void perror(const char *s)
- 使用说明
参数s指定的字符串是要先打印出来的信息,可以由我么自己定义,然后系统会在s字符串后加上错误原因的字符串。
三、 errno的线程/进程安全性
char * strerror(int errno)
fprintf(stderr, "%s", strerror(errno));
void perror(const char *s)
参数s指定的字符串是要先打印出来的信息,可以由我么自己定义,然后系统会在s字符串后加上错误原因的字符串。
想想吧,之前已经说了errno是全局变量,如果在多线程或者进程编程中,子线程想要查看errno确定自己的错误类型,可惜该errno被另一个线程更改了,这就出问题了。errno的线程/进程安全性指的就是这点,在多线程和多进程编程中,errno会不会变为线程或进程的私有变量。实际上,GCC就是这样处理的,在Ubuntu 16.04 64bit
系统上,代开/usr/include/x86_64-linux-gnu/bints/errno.h
中你会发现
When using threads, errno is a per-thread value
当使用线程时,errno会编程每个线程的值(每个线程都独有一个)
所以在并发编程中使用errno的时候一定要定义宏_LIBC_REENTRANT
附录
附上网友测试编译器是否支持errno相关宏的测试代码:
#include <stdio.h>
#include <errno.h>
int main( void )
{
#ifndef __ASSEMBLER__
printf( "Undefine __ASSEMBLER__\n" );
#else
printf( "define __ASSEMBLER__\n" );
#endif
#ifndef __LIBC
printf( "Undefine __LIBC\n" );
#else
printf( "define __LIBC\n" );
#endif
#ifndef _LIBC_REENTRANT
printf( "Undefine _LIBC_REENTRANT\n" );
#else
printf( "define _LIBC_REENTRANT\n" );
#endif
return 0;
}
参考三
源代码中\n
写成了/n
,已经更改