cin.eof() and feof(stdin) are discordant

2019-07-22 03:00发布

Can feof(stdin) and cin.eof() produce discordant results? I would expect them not to do so, but I guess I am missing something. Does the standard say anything about this?

Consider the following example, when no input is given. This can either be done by typing the shortcut for EOF in the terminal, or by redirecting the input on /dev/null (Unix), or with empty stdin sequence on ideone.com...

#include <cstdio>
#include <iostream>

int main() {
    using namespace std;

    char ch;
    cin >> ch;

    cin.clear();

    cout << boolalpha
         << "feof(stdin): " << bool(feof(stdin)) << endl
         << "cin.eof(): " << cin.eof() << endl;
}

Output:

feof(stdin): true
cin.eof(): false

标签: c++ input eof
1条回答
干净又极端
2楼-- · 2019-07-22 03:32

Well, there is no surprise here.

std::cin is truely defined in the C++ library, but stdin is defined in the C standard library that is member of the C++ one mainly for compatibility reasons.

Draft n4659 for C++17 says:

30.4.3 Narrow stream objects [narrow.stream.objects]

istream cin;

1 The object cin controls input from a stream buffer associated with the object stdin, declared in (30.11.1).

and later:

30.11 C library files [c.files]
30.11.1 Header <cstdio> synopsis [cstdio.syn]
...
1 The contents and meaning of the header <cstdio> are the same as the C standard library header <stdio.h>.

That means that cin is a C++ stream wrapping the underlying stdin C FILE object. As a result, when you try to read a character from cin after reaching the end of file, cin will ask it from stdin, stdin will report an end of file condition and both cin and stdin will set their internal end of file flag. Then the call to cin.clear() clears the C++ stream flag but it is unspecified whether it has an action on the stdin object internal flag.

So you are responsable for the lack of synchronization between the C++ cin object and the C stdin one.


Now for the details on what happens in this code copied from your ideone:

#include <cstdio>
#include <iostream>

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

int main() {
    using namespace std;

    char ch;
    cin >> ch;             // 1

    cin.clear();           // 2

    cout << boolalpha      // 3
         << "feof(stdin): " << bool(feof(stdin)) << endl
         << "cin.eof(): " << cin.eof() << endl;

    ch = char(getchar());  // 4
    cout << ch << endl;    // 5

    cout << boolalpha      // 6
         << "feof(stdin): " << bool(feof(stdin)) << endl
         << "cin.eof(): " << cin.eof() << endl;
}

//1 : you read a char from cin (ignoring the char): cin internally read from stdin, stdin finds an end of file, sets its internal EOF flag and return an end of file condition to cin. cin sets its own EOF flag

//2 : you clear the EOF flag on cin, stdin is unchanged (in this implementation since behaviour is unspecified by standard AFAIK)

//3 : as expected feof(stdin) is true and cin.eof() is false

//4 : you read a char from stdin (cin is untouched here). As you are already at end of file, getchar returns the integer constant EOF = -1. But as you force a conversion to char -1 is converted to '\xff', ASCII char DEL

//5 : '\xff' is not a valid UTF8 sequence and ideone displays it as the unicode REPLACEMENT CHARACTER �

//6 : feof(stdin) is still true: the C FILE has reached end of file, and cin.eof() is still false because nothing changed it since //3

TL/DR: everything is as expected: the underlying file as reached end of file and getchar() does return EOF, and the eof flags are set. Simply as you explicitely clear cin flags and never use it later cin.eof() is false.

查看更多
登录 后发表回答