In short, scanf()
as you've used it consumed the user's input character, but did not consume the newline he also had to type, leaving it for the next iteration of the loop to trip over. The easiest workaround is to just ignore newline characters from the user's input, by adding
case '\n': break;
to the switch
statement so that they are silently consumed rather than triggering the default
case.
That said, here is a more verbose explanation of what is going on under the hood:
In general, I/O operations are more complicated then beginner books and courses make them out to be. The natural form of I/O that the C runtime library promotes has the benefit that it hides most of the details of access to data behind the flexible concept of a "stream".
A stream is usually buffered, meaning that reads and writes to a stream actually act on an in-memory buffer so that the expensive system calls that actually move data to and from memory can happen less often and on more bytes at a time. When processing a large file, this is a huge benefit, and makes it practical to process entire files apparently a character at a time.
A fully buffered stream is really inconvenient for interactive use, however, so a typical implementation will make the stdin
, stdout
, and stderr
streams be "line buffered" when they are associated with an interactive terminal. When an output stream is line buffered, it flushes the buffer to the terminal when a newline is printed. When an input stream is line buffered, it fills the input buffer a line at a time.
That means in particular that the first call to scanf()
(or getchar()
or any other read operation on stdin
) will apparently block execution until the user has typed a newline, then proceed to return what it is expected to return.
In your case, scanf("%c", &input);
will block until the user typed a newline, then copy the first character typed to input
, leaving any additional characters and the newline in the buffer. The next time around the loop, scanf("%c", &input);
will take the next character from the buffer without waiting for any further input. We know there is at least one such character, because the first loop waited for say "d\n" to be typed but then consumed only the "d", leaving the "\n" for later.
The generally accepted best practice today would be to not use scanf()
at all. It would be much better to use fgets()
and then parse the entire line of input appropriately. For your example, you could read a line, then count all of the 'n', 'd', and 'q' occurrences. The details, of course, would depend on the specification of what you are supposed to implement.
I mentioned "buffered" and "line buffered" streams. There is a third kind of stream that is more suitable to interactive use. It is usually possible to make the stream itself be "unbuffered", such that every call to a stdio function takes effect immediately in the device or file. Unbuffered operation is only recommended for interactive programs, so to use it you should verify that stdin
really is attached to a terminal and not redirected from a file. You also need to deal with the fact that the terminal device driver itself is almost certainly going to try to at minimum line buffer its input. There will be a system-specific way to put the terminal driver into a raw mode where each character types will cause an immediate return from the read(2)
system call allowing a program to respond to individual keypresses without waiting for the carriage return key to be pressed. Actually configuring this mode of operation is tricky, platform specific, and well beyond the scope of this question.