stdin allows to read with the EOF flag set

2019-08-13 09:34发布

问题:

On my platform, the following code allows me to successfully read from stdin even if its end-of-file flag is set, which also remains set after the read. To reproduce the behavior, first type the end-of-file shortcut (Ctrl+D on Unix, Ctrl+Z on Windows) and then type a normal character.

#include <stdio.h>

// first type the shortcut for EOF, then a single character

int main(void) {
    getchar();
    printf("feof(stdin): %s\n", feof(stdin) ? "true" : "false");
    int ch = getchar();
    if (ch == EOF) return 0;
    printf("%c\n", (char) ch);
    printf("feof(stdin): %s\n", feof(stdin) ? "true" : "false");
}

The output I get (after typing the letter f):

feof(stdin): true
f
feof(stdin): true

From C11 standard (7.21.7.1, The fgetc function, 3):

Returns

If the end-of-file indicator for the stream is set, or if the stream is at end-of-file, the end-of-file indicator for the stream is set and the fgetc function returns EOF

getchar() is equivalent to getc(stdin) (7.21.7.6), which in turn is a macro for fgetc(stdin) (7.21.7.5). So getchar() should behave exactly like fgetc(stdin).

It seems to me that this is is not standard compliant. Am I missing something?


This question previously referred to C++ (thus the long discussion in comments), but the problem can be narrowed to the C standard library, thus I edited. Still the following information may be relevant:

The behavior is not consistent among platforms:

  • Arch Linux, GCC 7.3.1: can read after EOF;
  • Windows 7, GCC (Rev1, Built by MSYS2 project) 7.2.0: can read after EOF;
  • MacOS High Sierra, Apple LLVM version 9.0.0 (clang-900.0.39.2): cannot read after EOF;
  • FreeBSD 10.3, both clang 3.4.1 and GCC 5.4.0: cannot read after EOF.

This question is a follow-up of this one, which is about the fact the cin.clear() does not seem to unset the end-of-file flag of stdin, after some useful comments and chat discussion.

回答1:

On Linux this is indeed a known glibc bug in stdio implementation: #1190 - from 2005, #19476 - duplicate from 2016 which got fixed only in recent 2.28 build.