[C language] Mysterious scanf and %c: enter a phrase, not a character


I want to share a separate post with a very non-trivial task from the point of view of a beginner in C. It is the eighth self-monitoring question in chapter 6 of Stephen Prat’s C Primer Plus.

The task is:

What will the following programs output when you type Go west, young man! ?
(In ASCII, the ! character follows the space character.)

#include <stdio.h>
int main(void) {
  char ch;
  scanf("%c", &ch);
  while (ch != 'g') {
    printf("%c", ch);
    scanf("%c", &ch);
  }
  return 0;
}

It would seem that everything is simple. It should output G – after all, %c in scanf is a request for a single character. More precisely, even so – the program is written incorrectly, tk. we are entering the whole phrase with %c, when it should be entered as a string %s. Anyway. This is a tutorial and all, so let’s assume… So the answer is G.

It wasn’t there. The program outputs:

Go west, youn

How so? After poking around, I decided to lose my virginity on stackoverflow and created my first topic there. It turned out that this is the point (based on the answer of the respected Youssef13).

It is convenient to consider this example by adding line breaks:

#include <stdio.h>
int main(void)
{
  char ch;
  scanf("%c", &ch);
  while (ch != 'g')
  {
    printf("PRINTING: %c\n", ch);
    scanf("%c", &ch);
  }
  return 0;
}

The point is that scanf works this way – it maps to an “internal” cursor on stdin (standard input or standard input) that keeps track of what has been read. When we type Go west, young man!, the G character is stored in the ch variable, and the cursor in stdin moves one position, to the o character. Then, the next time we call scanf and it already looks at the cursor position – and there it is o.

If we want to ignore what was entered earlier in subsequent scanfs, we must read it (or use fseek on stdin, which works on Windows but will not work on Linux):

#include <stdio.h>
int main(void)
{
  char ch;
  scanf("%c", &ch);
  while (getchar() != '\n'); // Get to the end of the stdin.
// The previous line can be replaced with fseek(stdin, 0, SEEK_END);
// but it will work under Windows only.

  while (ch != 'g')
  {
    printf("PRINTING: %c\n", ch);
    scanf("%c", &ch);
  }
  return 0;
}

Happy to see your comments!


This entry was posted in C language (en). Bookmark the permalink.

Leave a Reply

🇬🇧 Attention! Comments with URLs/email are not allowed.
🇷🇺 Комментарии со ссылками/email удаляются автоматически.