errno全局变量及使用细则C语言errno全局变量完全攻略Word文件下载.docx
- 文档编号:20367343
- 上传时间:2023-01-22
- 格式:DOCX
- 页数:8
- 大小:44.14KB
errno全局变量及使用细则C语言errno全局变量完全攻略Word文件下载.docx
《errno全局变量及使用细则C语言errno全局变量完全攻略Word文件下载.docx》由会员分享,可在线阅读,更多相关《errno全局变量及使用细则C语言errno全局变量完全攻略Word文件下载.docx(8页珍藏版)》请在冰豆网上搜索。
fgetwc、fputwc
WEOF
EILSEQ
strtol、wcstol
LONG_MIN
或
LONG_MAX
ERANGE
stitoll、wcstoll
LLONG_MIN
LLONG_MAX
stitoul、wcstoul
ULONG_MAX
stitoull、wcstoull
ULLONG_MAX
stitoumax、wcstoumax
UINTMAX_MAX
strtod、wcstod
0
或±
HUGE_VAL
strtof、wcstof
HUGE_VALF
strtold、wcstold
HUGE_VALL
stitoimax、wcstoimax
INTMAX_MIN、INTMAX_MAX
如表1所示,这些函数将设置errno,并返回一个带内“In-Band”错误指示符。
例如,函数strtoul发生错误时将返回ULONG_MAX,并将errno的值设置为ERANGE。
这里就需要注意了,由于ULONG_MAX也是一个合法的返回值,因此必须使用errno来检查是否发生错误。
与此同时,对于这类函数,必须在调用这些库函数之前将errno的值设置为0,然后在调用库函数之后检查errno的值。
2)设置errno并返回一个带外“Out-of-Band”错误指示符的库函数
表2设置errno并返回一个带外“Out-of-Band”错误指示符的库函数
ftell()
-1L
positive
fgetpos()、fsetpos()
nonzero
mbitowc()、mbsrtowcs()
(size_t)(-1)
signal()
SIG_ERR
wcrtomb()、wcsitombs()
mbrtocl6()、mbitoc32()
c16rtomb()、cr32rtomb()
如表2所示,对于这类函数,应该先检查它的返回值,之后如果确实需要再继续检查errno的值。
3)不保证设置errno的库函数
例如,setlocale函数在发送错误时将返回NULL,但却不能保证一定会设置errno的值。
因此,在调用这类函数时,不应完全依赖于errno的值来确定是否发生了错误。
与此同时,该函数可能会设置errno的值,就算是这样也不能够保证errno会正确地提示错误的信息。
4)具有不同标准文档的库函数
有些函数在不同的标准中对errno有不同的定义。
例如,fopen函数就是一个典型的例子。
在C99中,并没有在描述fopen时提到errno,但是,POSIX.1却声明了当fopen遇到一个错误时将返回NULL,并且为errno设置一个值以提示这个错误。
调用errno之前必须先将其清零
在C语言中,如果系统调用或库函数被正确地执行,那么errno的值不会被清零。
换句话说,errno的值只有在一个库函数调用发生错误时才会被设置,当库函数调用成功运行时,errno的值不会被修改,当然也不会主动被置为0。
也正因为如此,在实际编程中进行错误诊断会有不少问题。
例如,在一段示例代码中,首先执行函数A的调用,如果函数A在执行中发生了错误,那么errno的值将被修改。
接下来,在不对errno的值做任何处理的情况下,继续直接执行函数B的调用,如果函数B被正确地执行,那么errno将还保留着函数A发生错误时被设置的值。
也正是这个原因,我们不能通过测试errno的值来判断是否存在错误。
由此可见,在调用errno之前,应该首先对函数的返回值进行判断,通过对返回值的判断来检查函数的执行是否发生了错误。
如果通过检查返回值确认函数调用发生了错误,那么再继续利用errno的值来确认究竟是什么原因导致了错误。
但是,如果一个函数调用无法从其返回值上判断是否发生了错误时,那么将只能通过errno的值来判断是否出错以及出错的原因。
对于这种情况,必须在调用函数之前先将errno的值手动清零,否则,errno的值将有可能够发生上面示例所展示的情况。
例如,当调用fopen函数发生错误时,它将会去修改errno的值,这样外部的代码就可以通过判断errno的值来区分fopen内部执行时是否发生错误,并且根据errno值的不同来确定具体的错误类型。
如下面的示例代码所示:
1.intmain(void)
2.{
3./*调用errno之前必须先将其清零*/
4.errno=0;
5.FILE*fp=fopen("
test.txt"
"
r"
);
6.if(errno!
=0)
7.{
8.printf("
errno值:
%d\n"
errno);
9.printf("
错误信息:
%s\n"
strerror(errno));
10.}
11.}
在这里,假设“test.txt”是一个根本不存在的文件。
因此,在调用fopen函数尝试打开一个并不存在的文件时将发生错误,同时修改errno的值。
这时,fopen函数会将errno指向的值修改为2。
我们通过stderror函数可以看到错误代码“2”的意思是“Nosuchfileordirectory”,如图3所示。
图3示例代码的运行结果
从上面的示例可以看出,使用errno来报告错误看起来似乎非常简单完美,但其实情况并非如此。
前面也阐述过,在C99中,并没有在描述fopen时提到errno。
但是,POSIX.1却声明了当fopen遇到一个错误时,它将返回NULL,并且为errno设置一个值以提示这个错误,这就暗示一个遵循了C99但不遵循POSIX的程序不应该在调用fopen之后再继续检查errno的值。
因此,下面的写法完全合乎要求:
3.FILE*fp=fopen("
4.if(fp==NULL)
5.{
6./*...*/
7.}
8.}
但是,上面也说过,在POSIX标准中,当fopen遇到一个错误的时候将返回NULL,并且为errno设置一个值以提示这个错误。
因此,在遵循POSIX标准中,应该首先检查fopen是否返回NULL值,如果返回,再继续检查errno的值以确认产生错误的具体信息,如下面的代码所示:
6.if(fp==NULL)
8.if(errno!
9.{
10.printf("
errno值:
11.printf("
%s\n"
12.}
13.}
14.}
其实,即使系统调用或者库函数正确执行,也不能够保证errno的值不会被改变。
因此,在没有发生错误的情况下,fopen也有可能修改的errno值。
先检查fopen的返回值,再检查errno的值才是正确的做法。
除此之外,建议在使用errno的值之前,必须先将其值赋给另外一个变量保存起来,因为很多函数(如fprintf)自身就可能会改变errno的值。
避免重定义errno
对于errno,它是一个由ISOC与POSIX标准定义的符号。
早些时候,POSIX标准曾经将errno定义成“externinterrno”这种形式,但现在这种定义方式比较少见了,那是因为这种形式定义的errno对多线程来说是致命的。
在多线程环境下,errno变量是被多个线程共享的,这样就可能引发如下情况:
线程A发生某些错误而改变了errno的值,那么线程B虽然没有发生任何错误,但是当它检测errno的值时,线程B同样会以为自己发生了错误。
我们知道,在多线程环境中,多个线程共享进程地址空间,因此就要求每个线程都必须有属于自己的局部errno,以避免一个线程干扰另一个线程。
其实,现在的大多部分编译器都是通过将errno设置为线程局部变量的实现形式来保证线程之间的错误原因不会互相串改。
例如,在Linux下的GCC编译器中,标准的errno在“/usr/include/errno.h”中的定义如下:
/*Gettheerrornumberconstantsfromthesystem-specificfile.
Thisfilewilltest__need_Emathand_ERRNO_H.
*/
#include<
bits/errno.h>
#undef
__need_Emath
#ifdef
_ERRNO_H
/*Declarethe`errno'
variable,unlessit'
sdefinedasamacrobybits/errno.h.
ThisisthecaseinGNU,whereitisaper-threadvariable.
Thisredeclarationusingthemacrostillworks,butitwillbeafunctiondeclarationwithoutaprototypeandmaytriggera-Wstrict-prototypeswarning.
#ifndef
errno
externinterrno;
#endif
其中,errno在“/usr/include/bits/errno.h”文件中的具体实现如下:
#ifndef__ASSEMBLER__
/*Functiontogetaddressofglobal'
errno'
variable.
externint*__errno_location(void)__THROW__attribute__((__const__));
#if!
defined_LIBC||defined_LIBC_REENTRANT
/*Whenusingthreads,errnoisaper-threadvalue.
#defineerrno(*__errno_location())
#endif
#endif/*!
__ASSEMBLER__*/
#endif/*_ERRNO_H*/
这样,通过“externint*__errno_location(void)__THROW__attribute__((__const__));
”与“#defineerrno(*__errno_location())”定义,使每个线程都有自己的errno,不管哪个线程修改errno都是修改自己的局部变量,从而达到线程安全的目的。
除此之外,如果要在多线程环境下正确使用errno,首先需要确保__ASSEMBLER__没有被定义,同时_LIBC没被定义或定义了_LIBC_REENTRANT。
可以通过下面的程序来在自己的开发环境中测试这几个宏的设置:
3.#ifndef__ASSEMBLER__
4.printf("
__ASSEMBLER__isnotdefined!
\n"
);
5.#else
6.printf("
__ASSEMBLER__isdefined!
7.#endif
8.#ifndef__LIBC
9.printf("
__LIBCisnotdefined\n"
10.#else
11.printf("
__LIBCisdefined!
12.#endif
13.#ifndef_LIBC_REENTRANT
14.printf("
_LIBC_REENTRANTisnotdefined\n"
15.#else
16.printf("
_LIBC_REENTRANTisdefined!
17.#endif
18.return0;
19.}
该程序的运行结果为:
__LIBCisnotdefined
_LIBC_REENTRANTisnotdefined
由此可见,在使用errno时,只需要在程序中简单地包含它的头文件“errno.h”即可,千万不要多此一举,在程序中重新定义它。
如果在程序中定义了一个名为errno的标识符,其行为是未定义的。
避免使用errno检查文件流错误
上面已经阐述过,在POSIX标准中,可以通过errno值来检查fopen函数调用是否发生了错误。
但是,对特定文件流操作是否出错的检查则必须使用ferror函数,而不能够使用errno进行文件流错误检查。
纯文本复制
3.FILE*fp=NULL;
4./*调用errno之前必须先将其清零*/
5.errno=0;
6.fp=fopen("
Test.txt"
w"
7.if(fp==NULL)
8.{
9.if(errno!
10.{
11./*处理错误*/
14.else
15.{
16./*错误地从fp所指定的文件中读取一个字符*/
17.fgetc(fp);
18./*判断是否读取出错*/
19.if(ferror(fp))
20.{
21./*处理错误*/
22.clearerr(fp);
23.}
24.fclose(fp);
25.return0;
26.}
27.}
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- errno 全局变量 使用 细则 语言 完全 攻略