I have found the example of clearing stdin using while((c = getchar()) != '\n' && c != EOF)
on here a few times, and tried to use it in a loop that gets input via fgets. I need to flush, since the loop takes the \n character from the last input and runs with it again.
So what happens is that I have to press enter twice now. Why is this happening, and how can I fix this?
#define BUFFER_LIMIT 50
do
{
int c;
while ((c = getchar()) != '\n' && c != EOF);
printf("console> ");
fgets(input_buffer, BUFFER_LIMIT-1, stdin);
if(do_something(input_buffer))
break;
} while(strncmp(input_buffer, "quit", 4) != 0);
There is unfortunately a lot of confusion about "flushing input" in C. The confusion arises almost entirely from one of the strange facts about the popular-but-flawed scanf
function: it generally does not read full lines of input, and it generally leaves the newline character \n
on the input stream after it converts its input. For example, if you write a program that says
printf("Type a number:\n");
scanf("%d", &n);
and if the user types "123" and hits the Return key, the number 123 will get stored into the variable n
, but the \n
character corresponding to the Return key will be left on the input stream. If the next thing your program does is to call fgets
, or getchar
, imagining that you'll begin reading the next line of input the user typed, your program will instead immediately read that leftover newline. It will seem as if the user typed an extra blank line, or something.
This problem is ridiculously widespread. Vast numbers of beginning C programmers have gotten stuck on it. In a nutshell, there are three recommended ways of fixing it:
After calling a function like scanf
that leaves the newline in the input buffer, and before calling a function like getchar
or fgets
that expects to start on a new line, use the little loop while((c = getchar()) != '\n' && c != EOF)
to read and discard the newline that scanf
left on the input buffer.
Don't use scanf
at all, or if you do, don't try to mix it with calls to getchar
or fgets
.
(popular but badly problematic) Call fflush(stdin)
to flush the unwanted input.
The problems with #3 have been widely discussed, so I won't say any more about those problems or that solution. The recommended "portable" alternative is #1, but it obviously works only if there is at least one unwanted newline in the input waiting to be flushed. If not, solution #1 will wrongly read and discard a line of wanted input, instead.
So you can't use #1 everywhere. You can't sprinkle it randomly in your program; you can't use it everywhere you might have used fflush(stdin)
. In general, as mentioned already, you'll need it only after calling scanf
and before calling getchar
or fgets
.
In the code fragment in the question, there may not have been a need to use solution #1 at all. The fgets
function is perfectly capable of cleanly reading individual lines of input all by itself. No additional flushing of input or discarding of newlines is necessarily required. (If there was a call to scanf
down underneath do_something()
, however, additional newline handling might have been necessary.)
So what happens is that I have to press enter twice now. Why is this happening, and how can I fix this?
Well, that is what your code is doing - it first reads char-by-char until it finds newline. Then it calls fgets()
which will... well, read until it finds a newline (probably char-by-char, but, also possibly in some other way).
You could try fflush(stdin)
, but that is not guaranteed to do what you want (it only gives guarantees for output buffers, not for input).
Also, you may try setbuf(stdin, NULL)
which should disable buffering on standard input, so there would be nothing to flush. I tried this a few times on different systems and it worked, but documentation for this function is not 100% clear on this.
My easy fix is making a buffer string char clear[2]
and flushing the input buffer by writing the newline character to it whenever I use scanf
by using gets(clear)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With