Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

In C, when stdin fails it works again in the future, the C ++ cin not?

65 views
Skip to first unread message

Christiano

unread,
Jan 25, 2017, 2:29:28 PM1/25/17
to
I come from the C language, see the following example:

// {======= get.c ============
#include <stdio.h>

int main(void)
{
char c;
char d;

c = getchar();
d = getchar();

putchar(c);

printf("\n");

putchar(d);

printf("\n");


return 0;
}
// }======= end get.c ========

Running (comments using notation ## comments ##):
debian@debian:~/principles$ gcc get.c -o get
debian@debian:~/principles$ ./get
##First I press Ctrl+D##
##Ctrl+D again##


debian@debian:~/principles$

That is, the two getchar tried to read the stdin and I pressed Ctrl + D to not feed the empty stdin.
Ok, Let's see something equivalent in C ++.

// {=========== get++.cpp ===========
#include <iostream>
using namespace std;

int main(void)
{
char c;
char v;

cin >> c;
cin >> v;

cout << c;

cout << endl;

cout << v;

cout << endl;

return 0;
}
// }=========== end get++.cpp ======

There is a difference in behavior here.
Running (comments using notation ## comments ##):
debian@debian:~/principles$ g++ get++.cpp -o get++
debian@debian:~/principles$ ./get++
##I press Ctrl+D just one time##

@
debian@debian:~/principles$

That is, the first crash of cin was enough to fail the second without giving me the chance to fill the stdin in the second time.
I just pressed Ctrl + D once and that was enough for cin >> v to fail.

Now, I will show a code based from book "Programming: Principles and Practice Using C++ Second Edition":
// {========= tdx.cpp ===========
#include "std_lib_facilities.h"

double some_function()
{
double d = 0;
cin >> d;
if(!cin)
error("Couldn't read a double in some_function()");

return d;
}

int main(void)
try {
double x = some_function();
cout << "The value read is: " << x << endl;
return 0;
}
catch(runtime_error &e) {
cerr << "runtime error: " << e.what() << "\n";
keep_window_open();

return 1;
}
// } ======= end tdx.cpp ========

Where std_lib_facilities.h is:
http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h

Running (comments using notation ## comments ##):

debian@debian:~/principles$ g++ tdx.cpp -o tdx
debian@debian:~/principles$ ./tdx
| ##the cin >> d wants a double, i will give "|" to force the fail##
runtime error: Couldn't read a double in some_function()
Please enter a character to exit
##Here he passed straight through without pausing.##
debian@debian:~/principles$

The program is designed to "pause" when the "double type" reading fails, before exiting.
But this is not what happens, as you can see.
Looking inside std_lib_facilities.h, you can see what keep_window_open () is:
inline void keep_window_open()
{
cin.clear();
cout << "Please enter a character to exit\n";
char ch;
cin >> ch;
return;
}

That is, "the first crash of cin was enough to fail the second without giving me the chance to fill the stdin in the second time." Again.

My question is: Is this a book error? Is this behavior normal?

I've scanned the pages, here:
http://imgur.com/a/8GEkE
http://imgur.com/a/dNWGe

Bo Persson

unread,
Jan 25, 2017, 2:45:03 PM1/25/17
to
This is by design. cin will remember the failure to read and will not
attempt to read anything more, until you call cin.clear() to clear the
error state.


Bo Persson


Alf P. Steinbach

unread,
Jan 25, 2017, 3:06:28 PM1/25/17
to
On 25.01.2017 20:29, Christiano wrote:
> I come from the C language, see the following example:
>
> // {======= get.c ============
> #include <stdio.h>
>
> int main(void)
> {
> char c;
> char d;
>
> c = getchar();
> d = getchar();
>
> putchar(c);
>
> printf("\n");
>
> putchar(d);
>
> printf("\n");
>
>
> return 0;
> }
> // }======= end get.c ========
>
> Running (comments using notation ## comments ##):
> debian@debian:~/principles$ gcc get.c -o get
> debian@debian:~/principles$ ./get
> ##First I press Ctrl+D##
> ##Ctrl+D again##
> �
> �
> debian@debian:~/principles$

Yep, the C FILE* streams do not have EOF as a state, they just report
EOF when they read in zero bytes – which is what happens when you press
Ctrl+D without having typed anything on the line, there is nothing, so
the read returns an empty sequence of bytes.

At one time long ago this was used in a “readslow” program to follow the
thinking of a famous chess program.

Wait let me google that. OK found something about it, not the chess
tournament thing, but it was mentioned in the old classic “The UNIX
Programming Environment” by Kernighan & Pike:


[TUPE]
> It is quite legal for several processes to be accessing the same file at the
> same time; indeed, one process can be writing while another is reading. If this
> isn’t what you wanted, it can be disconcerting, but it’s sometimes useful. Even
> though one call to read returns 0 and thus signals end of file, if more data is
> written on that file, a subsequent read will find more bytes available. This
> observation is the basis of a program called reads low, which continues to
> read its input, regardless of whether it got an end of file or not. reads low is
> handy for watching the progress of a program:
>
> $ slowprog >temp &
>
> 5213 Process-id
>
> $ readslow <temp I grep something
>
> In other words, a slow program produces output in a file; readslow, perhaps
> in collaboration with some other program, watches the data accumulate.
>
> Structurally, readslow is identical to cat except that it loops instead of
> quitting when it encounters the current end of the input. It has to use low-
> level I/O because the standard library routines continue to report EOF after the
> first end of file.
>
> /* readslow: keep reading, waiting for more */
>
> #define SIZE 512 /* arbitrary */
>
> main ( )
> {
> char buf [SIZE] ;
> int n;
>
> for (;;) {
> while ( (n = read( 0 , buf, sizeof buf)) > 0)
> write ( 1 , buf , n) ;
> sleep( 10 ) ;
> }
> }
>
> The function sleep causes the program to be suspended for the specified
> number of seconds; it is described in sleep(3). We don’t want readslow to
> bang away at the file continuously looking for more data; that would be too
> costly in CPU time. Thus this version of readslow copies its input up to the
> end of file, sleeps a while, then tries again. If more data arrives while it is
> asleep, it will be read by the next read.
[/TUPE]

Continuing with your posting:

On 25.01.2017 20:29, Christiano wrote:
> Ok, Let's see something equivalent in C ++.
>
> // {=========== get++.cpp ===========
> #include <iostream>
> using namespace std;
>
> int main(void)
> {
> char c;
> char v;
>
> cin >> c;
> cin >> v;
>
> cout << c;
>
> cout << endl;
>
> cout << v;
>
> cout << endl;
>
> return 0;
> }
> // }=========== end get++.cpp ======
>
> There is a difference in behavior here.

Yes, them dang iostreams are /stateful/. Once a stream gets into an
error state, which is a kind of “ignore all attempted operations” mode,
you have to clear it in order to continue.

In passing, `f(void)` is a C-ism. In C the `void` there ensures that the
function is declared as taking no arguments, as opposed to an arbitrary
number of arguments with just `f()`. But in C++ `f()` says directly that
it takes no arguments, there's no need for `f(void)`.
Yes. But it's a different issue. The `keep_window_open` function does
clear the stream error state, via a call to `clear`. But it fails to
empty the input buffer, where characters from the failed read still
linger. It could empty the input buffer with a call to `ignore`.

Check whether this bug is already in the book's errata list. If it isn't
then report it to Bjarne.


> Is this behavior normal?

Yes, it's by design.

But note that the `keep_window_open` issue is not the same as the end of
file state you encountered earlier.

For the first issue, about EOF, try out the following program:

#include <iostream>
#include <string>
using namespace std;

auto main()
-> int
{
double x;
cout << "? "; cin >> x;
if( cin.fail() )
{
cout << "Failed. This was left in input buffer: `";
cin.clear();
string s; getline( cin, s );
cout << s << "`." << endl;
}
else
{
cout << "Oh, the number " << x << "! Thanks! :)" << endl;
}
}


> I've scanned the pages, here:
> http://imgur.com/a/8GEkE
> http://imgur.com/a/dNWGe


Cheers & hth.,

- Alf

Alf P. Steinbach

unread,
Jan 25, 2017, 3:08:57 PM1/25/17
to
On 25.01.2017 21:05, Alf P. Steinbach wrote:
> For the first issue, about EOF, try out the following program:

I meant, for the second issue, about a failed read op. Grr.

Sorry 'bout that,

- Alf

Jorgen Grahn

unread,
Jan 25, 2017, 5:31:01 PM1/25/17
to
On Wed, 2017-01-25, Christiano wrote:
...
> debian@debian:~/principles$ gcc get.c -o get
> debian@debian:~/principles$ ./get
> ##First I press Ctrl+D##
> ##Ctrl+D again##
> ???
> ???
> debian@debian:~/principles$

You're describing feeding a process EOF on stdin more than once.
Can you see any practical use for this?

I have noticed the behavior, but I decided it has no practical use,
and that a program is better off honoring the EOF. Especially since
the behavior is limited to interactive use.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Alf P. Steinbach

unread,
Jan 25, 2017, 6:03:21 PM1/25/17
to
On 25.01.2017 23:30, Jorgen Grahn wrote:
> On Wed, 2017-01-25, Christiano wrote:
> ...
>> debian@debian:~/principles$ gcc get.c -o get
>> debian@debian:~/principles$ ./get
>> ##First I press Ctrl+D##
>> ##Ctrl+D again##
>> ???
>> ???
>> debian@debian:~/principles$
>
> You're describing feeding a process EOF on stdin more than once.
> Can you see any practical use for this?
>
> I have noticed the behavior, but I decided it has no practical use,

That may well be the case today. It wasn't always so. It was used.


> and that a program is better off honoring the EOF. Especially since
> the behavior is limited to interactive use.

Well, that's incorrect.

You can read more about it in my reply to the OP, earlier.

red floyd

unread,
Jan 25, 2017, 7:12:08 PM1/25/17
to
In addition to what everyone else has said, the value of c or v
if the read fails is completely undefined.


red floyd

unread,
Jan 25, 2017, 7:12:53 PM1/25/17
to
On 1/25/2017 4:11 PM, red floyd wrote:
[redacted]
> In addition to what everyone else has said, the value of c or v
> if the read fails is completely undefined.

Due to uninitialized local variable.


Ben Bacarisse

unread,
Jan 25, 2017, 7:30:05 PM1/25/17
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:

> On 25.01.2017 20:29, Christiano wrote:
>> I come from the C language, see the following example:
>>
>> // {======= get.c ============
>> #include <stdio.h>
>>
>> int main(void)
>> {
>> char c;
>> char d;
>>
>> c = getchar();
>> d = getchar();
>>
>> putchar(c);
>>
>> printf("\n");
>>
>> putchar(d);
>>
>> printf("\n");
>>
>>
>> return 0;
>> }
>> // }======= end get.c ========
>>
>> Running (comments using notation ## comments ##):
>> debian@debian:~/principles$ gcc get.c -o get
>> debian@debian:~/principles$ ./get
>> ##First I press Ctrl+D##
>> ##Ctrl+D again##
>> �
>> �
>> debian@debian:~/principles$
>
> Yep, the C FILE* streams do not have EOF as a state, they just report
> EOF when they read in zero bytes

A C FILE object does have a persistent end-of-file state which can be
tested using the feof function. The Language standard says that EOF (or
whatever other failure indicator is used) must be returned when that
indicator is set but it is cleared in a few situations: freopen, ungetc
and fseek being the only ones I can recall at the moment.

The comparatively well-known behaviour being described for the C input
library makes it technically non-conforming: C states that fgetc must
return EOF when the end-of-file indicator is set and a program that
prints that indicator shows a little more:

#include <stdio.h>

int main(void)
{
int c;
c = fgetc(stdin);
printf("1: %d\n", c);
if (feof(stdin)) {
c = fgetc(stdin);
printf("2: %d\n", c);
}
}

Closing the input with Ctrl-D then trying 'a' and enter gives:

1: -1
a
2: 97

<snip>
--
Ben.

Alf P. Steinbach

unread,
Jan 25, 2017, 7:40:31 PM1/25/17
to
I concede that I may be wrong about C. I just took the OP's example for
granted, reasoning that it had the same behavior as low-level Unix
calls. But your example shows the same, a /not/ persistent EOF state?

Cheers!, and a bit baffled,

- Alf


Ben Bacarisse

unread,
Jan 25, 2017, 7:56:00 PM1/25/17
to
The state is persistent but it's just ignored if a subsequent input
operation could succeed. But it's a system-specific corner case in that
as far as C is concerned the state is not only persistent but also it
must be honoured. The program above should not print the second line on
a conforming implementation.

--
Ben.

Christiano

unread,
Jan 26, 2017, 12:32:09 AM1/26/17
to
Thanks, Bo and Alf, I understood the problem by reading your answers.

The get++.cpp go straight through the second cin because "cin will remember the failure to read and will not attempt to read anything more, until you call cin.clear() to clear the error state. " (As said by Bo)

In tdx.cpp, the situation is different, "The `keep_window_open` function does
clear the stream error state, via a call to `clear`. But it fails to
empty the input buffer, where characters from the failed read still
linger." (As said by Alf)

Ben, this "new chance after EOF" exists since the classic programming books C, as highlighted by Alf. I did a search on standard C11 and found this function:

7.21.10 Error-handling functions
7.21.10.1 The clearerr function
Description
The clearerr function clears the end-of-file and error indicators for the stream pointed to by stream.

Jorgen Grahn

unread,
Jan 26, 2017, 10:53:22 AM1/26/17
to
On Wed, 2017-01-25, Alf P. Steinbach wrote:
> On 25.01.2017 23:30, Jorgen Grahn wrote:
>> On Wed, 2017-01-25, Christiano wrote:
>> ...
>>> debian@debian:~/principles$ gcc get.c -o get
>>> debian@debian:~/principles$ ./get
>>> ##First I press Ctrl+D##
>>> ##Ctrl+D again##
>>> ???
>>> ???
>>> debian@debian:~/principles$
>>
>> You're describing feeding a process EOF on stdin more than once.
>> Can you see any practical use for this?
>>
>> I have noticed the behavior, but I decided it has no practical use,
>
> That may well be the case today. It wasn't always so. It was used.

The "readslow" example would, I suspect, translate to Unix "tail -f"
today. And I do indeed use that a lot.

On the other hand, I don't ever want to see programs which treat EOF
as a record separator on standard input. In that sense, it still
seems to me the feature is of use in very specific situations.

>> and that a program is better off honoring the EOF. Especially since
>> the behavior is limited to interactive use.
>
> Well, that's incorrect.

The tail -f example, yes.

> You can read more about it in my reply to the OP, earlier.

The one quoting "The Unix Programming Environment". Yes, and thanks.

Tim Rentsch

unread,
Jan 27, 2017, 3:04:42 AM1/27/17
to
Ben Bacarisse <ben.u...@bsb.me.uk> writes:

> "Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>
>> On 26.01.2017 01:29, Ben Bacarisse wrote:
>>> "Alf P. Steinbach" <alf.p.stein...@gmail.com> writes:
>>>
>>>> On 25.01.2017 20:29, Christiano wrote:
>>>>> I come from the C language, see the following example:
>>>>>
>>>>> // {======= get.c ============
>>>>> #include <stdio.h>
>>>>>
>>>>> int main(void)
>>>>> {
>>>>> char c;
>>>>> char d;
>>>>>
>>>>> c = getchar();
>>>>> d = getchar();
>>>>>
>>>>> putchar(c);
>>>>>
>>>>> printf("\n");
>>>>>
>>>>> putchar(d);
>>>>>
>>>>> printf("\n");
>>>>>
>>>>>
>>>>> return 0;
>>>>> }
>>>>> // }======= end get.c ========
>>>>>
>>>>> Running (comments using notation ## comments ##):
>>>>> debian@debian:~/principles$ gcc get.c -o get
>>>>> debian@debian:~/principles$ ./get
>>>>> ##First I press Ctrl+D##
>>>>> ##Ctrl+D again##
>>>>> ?
>>>>> ?
> The state is persistent but its just ignored if a subsequent input
> operation could succeed. But its a system-specific corner case in that
> as far as C is concerned the state is not only persistent but also it
> must be honoured. The program above should not print the second line on
> a conforming implementation.

Just a short comment here. I agree with your analysis, but note
that the Standard's description regarding EOF changed between C90
and C99. In C90 I believe the observed behavior is conforming,
or at least arguably conforming. (Let me add that I have not
gone through any DR's or other documents to see when the newer
requirements may have come into force.)
0 new messages