苏联沙皇氢弹爆炸视频:第三篇:errno.h快速入门
来源:百度文库 编辑:九乡新闻网 时间:2024/04/27 23:58:01
第三篇:errno.h快速入门
简介:
头文件errno.h定义了一个全局的宏errno,它被展开为一个int类型的“左值”,这意味着宏errno不一定是个对象的标识符,也可以展开为一个由函数返回的可以修改的“左值”,比如int*errno(),这个后面会讲,你可以暂且把它理解为一个全局的int型变量(虽然这样理解是错的,不过方便理解)。
简单来说,errno.h只是为了提供了一种错误报告机制。比如,一个函数调用fopen()发生了错误,它可能就会去修改errno的值,这样外部的代码可以通过判断errno的值来区分fopen()内部执行时是否发生错误,并根据errno值的不同来确定具体的错误类型。
先来看一段代码,Demo1:
01
#include
02
#include
03
04
int
main (
int
argc,
char
*argv[] )
05
{
06
//try to open file "whatever.txt". when you run this demo,make sure the file is NOT existed..。
07
FILE
*fp =
fopen
(
"whatever.txt"
,
"r"
);
08
09
if
(fp ==NULL)
10
{
11
printf
(
"Can not open file\n"
);
12
13
printf
(
"errno value: %d, it means: %s"
,
errno
,
strerror
(
errno
));
14
}
15
16
return
0;
17
}
程序会输出:
view sourceprint?1
Can not
open
file
2
errno value: 2, it means: No such
file
or directory
strerror是标准库stdio.h定义的一个函数,它用来返回错误代码所代表的含义。如Demo1所示,我们用fopen(也在stdio.h中定义)打开一个并不存在的文件,因此返回的fp是一个空指针。而在fopen尝试打开文件失败时会修改errno的值,Demo1里fopen失败原因是文件不存在,因此fopen将会把errno指向的值修改为2,通过stderror可以看到错误代码2的意思是“No suchfile or directory”。
帅气,看起来用errno来报告错误,既方便,也很简单,但实际应用时远没Demo1那么简单,试下Demo2:
view sourceprint?01
#include
02
#include
03
#include
04
05
int
main (
int
argc,
char
*argv[] )
06
{
07
/*try to open file "whatever.txt". when you run this demo,make sure the file is NOT existed...*/
08
FILE
*fp =
fopen
(
"whatever.txt"
,
"r"
);
09
10
if
(fp ==NULL)
11
{
12
printf
(
"Can not open file\n"
);
13
14
int
root =
sqrt
(123568 -123668 );
15
16
printf
(
"errno value: %d, it means: %s"
,
errno
,
strerror
(
errno
));
17
}
18
19
return
0;
20
}
程序会输出:
view sourceprint?1
Can not
open
file
2
errno value: 33, it means: Numerical argument out of domain
这跟我们期望的错误信息完全不一样,因为sqrt函数在得到一个非法的参数时,它把errno的值修改成了33,覆盖了fopen设置的错误代码。所以,使用errno一个比较安全的编码方式是,在下个库函数调用之前就查看它的值,千万不要因为某个库函数看似很简单就假设它不会修改errno的值,那样会死的很惨……如果必须在查看errno之前调用别的库函数,一种安全的方式是先把errno的值保存到一个临时变量里,然后调用那个“必须”调用的库函数,处理完毕后再把errno恢复到之前的值。如Demo3:
view sourceprint?01
#include
02
#include
03
#include
04
05
double
getSqrt(
double
value)
06
{
07
//1、save last errno to a temp variable
08
int
tmpErrno =
errno
;
09
//2、set errno to 0
10
errno
= 0;
11
12
double
root =
sqrt
(123568 -123668 );
13
14
printf
(
"I changed errno to '%d' sliently...but it's safe \n"
,
errno
);
15
16
//3.restore errno
17
errno
= tmpErrno;
18
19
return
root;
20
}
21
22
int
main (
int
argc,
char
*argv[] )
23
{
24
FILE
*fp =
fopen
(
"whatever.txt"
,
"r"
);
25
26
if
(fp ==NULL)
27
{
28
printf
(
"Can not open file\n"
);
29
30
getSqrt(-1);
31
32
printf
(
"errno value: %d, it means: %s"
,
errno
,
strerror
(
errno
));
33
}
34
35
return
0;
36
}
程序会输出:
view sourceprint?1
Can not
open
file
2
I changed errno to
'33'
sliently...but it's safe
3
errno value: 2, it means: No such
file
or directory
现在,就像期望的那样输出了。
但是……
这样真的安全么?!想像一下,如果errno是个全局变量,那多线程环境下岂不完蛋了?!本来线程A把errno设置成2,还没执行到查看错误的语句时,线程B就把errno设置成了33,然后线程A才开始查看errno并输出错误信息,而这时输出的错误就很让人抓狂了!神呀,这破东西多线程没法儿用哇!
但是……
你多虑了……文章开始说过,宏errno可以被展开为一个“左值”,比如int*getYourErrno(),所以你可以在getYourErrno()里返回一个线程内的局部变量,这样不管哪个线程修改errno都修改的它自己的局部变量,所以我们担心的问题是不存在的。看下errno.h的源码就明白了
01
/* Get the error number constants from the system-specific file.
02
This file will test __need_Emath and _ERRNO_H. */
03
#include
04
#undef __need_Emath
05
06
#ifdef _ERRNO_H
07
08
/* Declare the `errno' variable, unless it's defined as a macro by
09
bits/errno.h. This is the case in GNU, where it is a per-thread
10
variable. This redeclaration using the macro still works, but it
11
will be a function declaration without a prototype and may trigger
12
a -Wstrict-prototypes warning. */
13
#ifndef errno
14
extern
int
errno
;
15
#endif
上面的注释说了,如果errno没有定义过就把errno定义为“extern interrno;”,如果这样多线程时是会发生悲剧的,先不着急哭,我们去前面看看它是否被定义过,前面的代码include了一个叫bits/errno.h的头文件,看名字就很“险恶”,进去看看:
view sourceprint?1
# ifndef __ASSEMBLER__
2
/* Function to get address of global `errno' variable. */
3
extern
int
*__errno_location (
void
) __THROW __attribute__ ((__const__));
4
5
# if !defined _LIBC || defined _LIBC_REENTRANT
6
/* When using threads, errno is a per-thread value. */
7
# define errno (*__errno_location ())
8
# endif
9
# endif /* !__ASSEMBLER__ */
果然来者不善……如果没定义宏__ASSEMBLER__,就会执行中间的代码,里面的代码又说了,如果你没定义_LIBC或者定义了_LIBC_REENTRANT他们就会把errno定义为__errno_location,一看到宏_LIBC_REENTRANT里面有“reentrant(重入)”就知道它不是个好东西……不是,是看到它的名字就知道它是跟多线程有关的,所以如果要在多线程环境下正确的使用errno,你需要确保__ASSEMBLER__没有被定义,而且_LIBC没被定义或者定义了_LIBC_REENTRANT。
可以写个程序看下自己开发环境里这几个宏的设置:
01
#include
02
#include
03
04
int
main(
void
)
05
{
06
#ifndef __ASSEMBLER__
07
printf
(
"__ASSEMBLER__ is NOT defined!\n"
);
08
#else
09
printf
(
"__ASSEMBLER__ is defined!\n"
);
10
#endif
11
12
#ifndef __LIBC
13
printf
(
"__LIBC is NOT defined\n"
);
14
#else
15
printf
(
"__LIBC is defined!\n"
);
16
#endif
17
18
#ifndef _LIBC_REENTRANT
19
printf
(
"_LIBC_REENTRANT is NOT defined\n"
);
20
#else
21
printf
(
"_LIBC_REENTRANT is defined!\n"
);
22
#endif
23
24
return
0;
25
}
我的输出:
view sourceprint?1
_ASSEMBLER__ is NOT defined!
2
__LIBC is NOT defined!
3
_LIBC_REENTRANT is NOT defined!
哈!看来我可以在多线程下安全的使用errno,如果你的默认环境不可以就在makefile里定义上_LIBC_REENTRANT吧!
ok,有关errno.h的介绍到此就结束,休息,休息一下!
来源:http://blog.yaohuiji.com/
欢迎转载!作者期望转载时带上原文链接,不过这不是必须的。但务必在文章标题处标明【转载】