为什么errno可以通过scanf设置为零(当input“ctrl + D”时)

手册告诉我们:errno从来不会被任何系统调用或库函数设置为零。 但是我想知道,为什么在下面的代码中,errno可以通过scanf设置为零(当scanf:input“ctrl + D”时)

#include <stdio.h> #include <errno.h> int main() { int i; errno = 5; printf("errno:%d\n",errno); if (scanf("%d", &i) < 1) perror("scanf"); printf("errno:%d\n",errno); printf("i:%d\n", i); return 0; } 

我可以在执行scanf()调用的vfscanf()的glibc实现中找到下面的代码(在编写的时候,链接文件中的589-607行scanf()

 if (skip_space || (fc != L_('[') && fc != L_('c') && fc != L_('C') && fc != L_('n'))) { /* Eat whitespace. */ int save_errno = errno; __set_errno (0); do /* We add the additional test for EOF here since otherwise inchar will restore the old errno value which might be EINTR but does not indicate an interrupt since nothing was read at this time. */ if (__builtin_expect ((c == EOF || inchar () == EOF) && errno == EINTR, 0)) input_error (); while (ISSPACE (c)); __set_errno (save_errno); ungetc (c, s); skip_space = 0; } 

input_error()#define d as:

 #define input_error() do { errval = 1; if (done == 0) done = EOF; goto errout; } while (0) 

errout是最后清理代码的标签。

所以看起来在inchar()调用之前, errno被设置为0 ,并且旧的值被替换,使得errno保持不变。 但是,如果发生错误,并执行语句(特别是,如果inchar()计算为EOF ,这是在这种情况下发生的),它看起来像重置errno到其原始值的代码可能会被跳过。 这就是说,条件只会是真正的,如果errno == EINTR ,因此不是零,在这里肯定不会是这样,所以这可能与这个代码无关,但这只是我的地方可以看到errno被设置为0 。 正如注释所示, inchar()本身确实包含了errno ,并且可以将errnoinchar_errno ,在第223 inchar_errno其初始化为0 ,所以也可能存在其他一些执行路径,其中inchar_errno未更新,但被分配给errno无论如何。

这是一个错误。 你应该报告 。 (该页面用于GCC bug,GCC只提供标准库的一部分;我不确定scanf是否是GCC的一部分,红帽bug报告系统在这里) 。

每个C 2011(N1570)7.5 3:“在程序启动时errno在初始线程中的值为零(其他线程中的errno的初始值是一个不确定的值),但不会被任何库函数设置为零。

N1570并不是标准的正式版本,但它是非常接近的,1999版标准的正式版本没有线程方面的规格是相同的:“在程序启动时,errno的值为零,但从未设置为零任何库函数“。