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

时间:2013-10-20 12:28:58

标签: c linux

手册告诉我们:任何系统调用或库函数都不会将errno设置为零。但是我想知道,为什么errno可以通过以下代码中的scanf设置为零?(当scanf:输入“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;
}

2 个答案:

答案 0 :(得分:3)

我可以在glibc implementation of vfscanf()中找到以下代码,(截至撰写本文时,链接文件中的第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为:

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

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

因此看起来errno0调用之前设置为inchar(),旧值后来被替换,errno保持不变。但是如果发生错误并且执行了if语句(特别是,如果inchar()计算为EOF,这就是这种情况下发生的情况),它看起来像要重置的代码{{可以跳过1}}到其原始值。话虽如此,只有errno并且因此不是零时才会出现这种情况,这在某些情况下似乎并非如此,所以它可能与此代码无关,但这只是一个地方我可以看到errno == EINTR设置为errno。正如评论所示,0本身会混淆inchar(),并且可以将errno设置为errno,在第223行初始化为inchar_errno,所以也有可能还有一些其他执行路径0未更新,但无论如何都被分配到inchar_errno

答案 1 :(得分:2)

这是一个错误。你应该report it。 (该页面用于GCC错误.GCC仅提供部分标准库;我不确定scanf是否是GCC的一部分.Red Hat错误报告系统是here。)

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

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