2
votes

I was finishing up K&R exercises 7.4 and 7.5 and came across an annoying "feature" that I don't believe the standard states.

According to the K&R, the mode of action for the conversion specification "%c"

"The next input characters (default 1) are placed at the indicated spot. The normal skip over white space is suppressed; to read the next non-white space character, use %1s"

My question is, is that statement supposed to be read like:

"The next input characters (default 1) are placed at the indicated spot. THEN, in successive calls to scanf in which %c is used again, the normal skip over white space is suppressed; to read the next non-white space character, use %1s"

...because this code:

void test1()
{
   char t1, t2;

   scanf("%c %c", &t1, &t2);
   printf("%d\n", t1);
   printf("%d\n", t2);

   //INPUT is: "b d" (without quotes)
}

results in t1 = 98 (b) and t2 = 100 (d). (Whitespace skipped)

However, this code:

void test2()
{
   char t1, t2;

   scanf("%c", &t1);
   scanf("%c", &t2);
   printf("%d\n", t1);
   printf("%d\n", t2);

   //INPUT is: "b d" (without quotes)
}

results in t1 = 98 (b) and t2 = 32 (' '). (Whitespace NOT skipped)

Reading the original quote, I think any reasonable person would take it to mean that during that same call to scanf(%c), the whitespace skip is suppressed. However, that doesn't seem to be the case.

It seems that in order to gain back the original functionality, one would have to completely empty stdin.

Is this supposed to work this way? Has it been documented? Because i've looked around and haven't seen much information on this.

For reference, I'm programming in C99.

6

6 Answers

13
votes

This is because a space in the string passed to scanf means whitespace skip. If you removed the space and used "%c%c" instead of "%c %c", the first program would behave exactly as the second.

So the answer to your question is: the normal skip is always suppressed, it's just the space that does the magic.

3
votes

The man page for scanf on Linux states:

c     Matches a sequence of characters whose length is specified by the maximum field width (default 1); the next pointer must be a pointer to char, and there must be enough room for all the characters (no terminating null byte is added). The usual skip of leading white space is suppressed. To skip white space first, use an explicit space in the format.

I believe that removes the ambiguity: c by itself does not skip whitespace; you must have an explicit space character in the format string. Therefore if you change your second example to:

scanf("%c", &t1);
scanf(" %c", &t1);

The second call to scanf will skip whitespace, because of the explicit whitespace character.

2
votes

The blank between "%c %c" means skip whitespace between first and second character, "%c" in first place means read any character.

2
votes

No -- %c just reads the next character of input, regardless of what it is. The contrasts with %s, which skips any amount of leading white space, then reads up to the specified number of characters (stopping at the specified number or when it encounters a whitespace character).

In your first piece of code, however, you have a space in the format string between the two %c conversions. A space in a format stream means it should skip across any and all successive white space before attempting the next conversion.

0
votes

From the scanf man page

A directive is one of the following:

  • A sequence of white-space characters (space, tab, newline, etc; see isspace(3)). This directive matches any amount of white space, including none, in the input.

etc. In the first case you have this directive but not in the second.

0
votes

The standard (C9899:TC3) says that for %c and some other specifications whitespace is not skipped (7.19.6.2.8).