Here, I saw this statement in the accepted answer:
Most of the conversion specifiers skip leading whitespace including newlines but
%c
does not.
For me it is not clear the rationale under this different behaviors, I would have expected a uniform one (e.g. always skipping or never).
I came into this kind of problem with a piece of C code like this:
#include "stdio.h"
int main(void){
char ch;
int actualNum;
printf("Insert a number: ");
scanf("%d", &actualNum);
// getchar();
printf("Insert a character: ");
scanf("%c", &ch);
return 0;
}
Swapping the two scanf
s solves the problem, as well as the (commented) getchar
, otherwise the '\n'
of the first insertion would be consumed by the second scanf
with %c
. I tested on gcc both on linux and windows, the behavior is the same:
gcc (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
So my question is: Why does %d
and %c
behave differently w.r.t. '\n'
in scanf
?
It is consistent behavior, you're just thinking about it wrong. ;)
scanf("%c", some_char); // reads a character from the key board.
scanf("%d", some_int); // reads an integer from the key board.
So if I do this:
printf("Insert a character: ");
scanf("%c", &ch); // if I enter 'f'. Really I entered 'f' + '\n'
// scanf read the first char and left the '\n'
printf("Insert a number: ");
scanf("%d", &actualNum); // Now scan if is looking for an int, it sees the '\n'
// still, but that's not an int so it waits for the
// the next input from stdin
It's not that it's consuming the newline on its own in this case. Try this instead:
char ch;
char ch2;
printf("Insert a character: ");
scanf("%c", &ch);
printf("Insert another character: ");
scanf("%c", &ch2);
It will "skip" the second scanf()
because it reads in the '\n'
at that time. scanf()
is consistent and you MUST consume that '\n'
if you're going to use it correctly.
From the horse's mouth:
7.21.6.2 The fscanf function
...
5 A directive composed of white-space character(s) is executed by reading input up to the first non-white-space character (which remains unread), or until no more characters can be read. The directive never fails.
...
7 A directive that is a conversion specification defines a set of matching input sequences, as described below for each specifier. A conversion specification is executed in the following steps:
8 Input white-space characters (as specified by theisspace
function) are skipped, unless the specification includes a[
,c
, orn
specifier. 284)
9 An input item is read from the stream, unless the specification includes ann
specifier. An input item is defined as the longest sequence of input characters which does not exceed any specified field width and which is, or is a prefix of, a matching input sequence. 285) The first character, if any, after the input item remains unread. If the length of the input item is zero, the execution of the directive fails; this condition is a matching failure unless end-of-file, an encoding error, or a read error prevented input from the stream, in which case it is an input failure.
284) These white-space characters are not counted against a specified field width.
285) fscanf pushes back at most one input character onto the input stream. Therefore, some sequences that are acceptable to strtod, strtol, etc., are unacceptable to fscanf.
Emphasis added by me.
Whitespace is not part of a valid integer string, so it makes sense for the %d
conversion specifier to skip any leading whitespace. However, whitespace may be valid on its own, so it makes sense for the %c
conversion specifier to not skip it.
Per clause 5 above, if you put a blank space in the format string prior to the %c
directive, all leading whitespace will be skipped:
scanf(" %c", &ch);
It is because whitespace can never be an integer, but whitespace is made up of characters. If you specifically want a character try something like
scanf("%c", &ch );
while(isspace(c))
scanf("%c" , &ch);
Or use !isalnum()
if you want to allow only letters and numbers or !isalpha()
for only letters.
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