Reputation:
I'm not sure if I properly understand how flushing works in C. I just can't get it to work as described in multiple manuals and reference books. Here's an example with comments:
#include <stdio.h>
int main(void) {
int x;
char ch;
printf("Prompt: ");
scanf("%d", &x); /* I key in 67 and press Enter. At this very moment,
the input buffer should contain the ASCII codes
for the numbers 6 and 7 as well as the ASCII code
for the newline character, which is 10. The scanf
function is going to find the ASCII codes for 6
and 7, convert them to the integer 67, assign it
to the variable x and remove them from the
buffer. At this point, what still remains in the
buffer is the newline character. */
fflush(stdin); /* I want to do some more input. So, I flush the
buffer to remove anything that still might be
there, but it doesn't really look like fflush is
doing that. */
printf("Prompt: "); /* I'm not going to be able to get my hands on
the following line of code because fflush is
not doing its job properly. The remaining
newline character is going to be read into the
variable ch automatically, thus not allowing
me to enter anything from the keyboard. */
scanf("%c", &ch);
printf("x: %d, ch: %d\n", x, ch);
/*
OUTPUT:
Prompt: 67
Prompt: x: 67, ch: 10
*/
return 0;
}
Upvotes: 1
Views: 1037
Reputation: 70213
Aside from fflush()
not being defined on input streams as Sourav pointed out...
I key in 67 and press Enter. At this very moment, the input buffer should contain the ASCII codes for the numbers 6 and 7 as well as the ASCII code for the newline character, which is 10. The scanf function is going to find the ASCII codes for 6 and 7, convert them to the integer 67, assign it to the variable x and remove them from the buffer. At this point, what still remains in the buffer is the newline character.
And if the user did input something that is not a number, other things could have happened.
If there were non-digits before the first digit, x
will not be initialized. You wouldn't know, because you did not check the return value
of scanf()
.
The user might have anticipated the next prompt (for a character), and have entered something like 67 x
, expecting the 67
to satisfy your first prompt, and the x
your second. (And he won't be happy your program dropped part of his entry.)
Using *scanf()
on input you cannot be sure is of the expected format (user input, as opposed to e.g. reading back something you yourself wrote with *printf()
) is fragile.
So my generic advice is to not use the *scanf()
functions on user input, but instead use fgets()
to read user input line-wise, and parse the input in-memory at your leisure.
You have many much more powerful functions at your disposal that way, and you can handle error conditions more fine-grained (including the ability to give the full line entered in any error messages).
The below is only a rough scetch; depending on your applications, you'll want to organize this differently:
const size_t BUFFERSIZE = 1024;
char buffer[ BUFFERSIZE ];
long x;
char ch;
printf( "Prompt: " );
if ( fgets( buffer, BUFFERSIZE, stdin ) == NULL )
{
// Error occurred, use feof() / ferror() as appropriate
}
else
{
size_t len = strlen( buffer );
if ( buffer[ len - 1 ] != '\n' )
{
// The line entered was too long for the buffer,
// there is unread input. Handle accordingly, e.g.
// resizing the buffer and reading the rest.
}
else
{
// You got the whole line; blot out the newline
buffer[ len - 1 ] = '\0';
// Assuming a decimal number first
char * curr = buffer;
errno = 0;
long x = strtol( curr, &curr, 10 );
if ( errno == ERANGE )
{
// Number exceeds "long" range, handle condition
}
else if ( curr == buffer )
{
// What you got was not a number, handle condition
}
// Keep parsing until you hit end-of-string
}
}
Upvotes: 1
Reputation: 8657
For completion's sake:
Some implementations do define fflush
on input streams. Examples are Microsoft's msvcrt and GNU libc. Others, like the BSD libc, may additionally provide a separate function for purging the buffer.
These functions, beside being unportable, have some serious drawback: They are often used with the assumption that the buffer looks some particular way, e.g. has a single newline.
stdin
may be connected to a file, and its buffer might contain more than just a newline. These buffered data would be skipped over, if the input stream is purged.
Further, there can be buffers outside the libc's stdio
which aren't affected.
Therefore, you should explicitly read and discard the data you don't want.
Upvotes: 2
Reputation: 134286
Don't do fflush(stdin);
, it invokes undefined behavior.
Quoting C11
,
Ifstream points to an output stream or an update stream in which the most recent operation was not input, the fflush function causes any unwritten data for that stream to be delivered to the host environment to be written to the file; otherwise, the behavior is undefined.
and stdin
is not an output stream.
Upvotes: 4