lilroo
lilroo

Reputation: 3128

fgetc(stdin) in a loop is producing strange behaviour

I have this code

while(1){
    printf("hello world !\n");
    fgetc(stdin);
}

when this runs and I enter a letter like this:

hello world !
a

it ignores the fgetc(stdin) in the next loop and prints hello world twice without waiting for input.

hello world !
a
hello world !
hello world !
a
hello world !
hello world !
a
hello world !
hello world !
a
hello world !
hello world !

I have tried putting fflush(stdin) before or after the fgetc(stdin) but it still produces the same behaviour, what am I doing wrong ?

Upvotes: 8

Views: 28051

Answers (4)

Adam Zalcman
Adam Zalcman

Reputation: 27233

That's because you actually enter two characters: 'a' and a newline. Also, since terminal is normally line-buffered your program will only see your input once you hit the newline. It'll be informative to enter a longer line of text, too.

If you want to change this behavior you have two options: reading entire lines (i.e. all characters up to a newline or end-of-file) or switching terminal to non-canonical mode. The latter makes sense if you're working on an interactive terminal application like a text editor. See termios manpage for details. In short, you'll want to set MIN and TIME options to zero to make reads from terminal return immediately as data becomes available. If you do go down this path, make sure you switch the terminal back when you exit, including due to reception of a signal.

fflush() affects the output, not the input.

Upvotes: 10

cegfault
cegfault

Reputation: 6632

There are two characters: a and \n (newline). Your loop reads reads the a, then loops and prints "hello world !". It then sees \n and loops and prints "hello world !". When you type a+\n in the terminal, it's storing the two characters in the stdin buffer. fgetc(stdin); will read from the stdin buffer if there is a char available, otherwise it waits until a char is added to the buffer.

Since terminals are line-buffered (ie, do not send the content to the program until a newline is reached) you have a few options:

  • read the entire line into a buffer, but take only the first character
  • ignore newlines
  • turn off line-buffering

To turn off line buffering, look at http://c-faq.com/osdep/cbreak.html and http://www.flipcode.com/archives/_kbhit_for_Linux.shtml and http://ubuntuforums.org/showthread.php?t=225713 (although I have not tested any of the code here).

Upvotes: 3

Cacho Santa
Cacho Santa

Reputation: 6914

You have two characters there, 'a' and '\n'. That is the problem, because fgetc will only read ONE character. This is the documentation.

If you enter only a '\n' - hit enter only - you will have the expected behaviour.

Hope it helps!

Upvotes: 2

Dancrumb
Dancrumb

Reputation: 27539

Terminals tend to be line-buffered, meaning that stream contents are accessible on a line-by-line basis.

So, when fgetc starts reading from STDIN, it's reading a full line, which includes the newline character that ended that line. That's the second character you're reading.

As for fflush, that's for flushing output buffers, not input buffers.

So, what you want to do here is to purge the input buffer by reading from it until its empty, or just explicitly ignore newline characters.

Upvotes: 7

Related Questions