1

In the following snippet, after reading the int the '\n' remains in the stdin, and is read by second scanf.

Is scanf called on the enter, and then reads what is in the stdin, or is it called before typing happens?

What signals to scanf that the input is ready? For example if I type on my keyboard 12345, and scanf is reading an int, it can be read as 1, 12, 123 ... If the enter is the signal to read, why doesn't scanf clear that character from the stdin?

#include <stdio.h>

int main()
{
    int a;
    scanf( "%d", &a );
    
    char b;
    scanf( "%c", &b );
    
    printf( "%d %c", a, b );
    return 0;
}
paulsm4
  • 114,292
  • 17
  • 138
  • 190
mdjukan
  • 41
  • 3
  • 1
    In the snippet, `b` will read the `'\n'` left by the entry of `a`. To correct use `" %c"` as the format string where the leading whitespace in the format string will consume the whitespace (`'\n'` being whitesapace as is `space`, `tab`, etc..) Always validate the return of `scanf()`. The return is the number of successful conversions that take place. So `if (scanf ("%d", &a) == 1)` you know a valid integer was provided. (validation with `"%c"` isn't needed except to check for `EOF`.) – David C. Rankin Dec 18 '21 at 02:24
  • Thanks for the answer! Is scanf called before the '\n', and waits for it? – mdjukan Dec 18 '21 at 02:26
  • `scanf` doesn't consume trailing whitespace. The comment above gives you the work around to consume it before the next conversion. – DekuDesu Dec 18 '21 at 02:27
  • When you press **[Enter]** after the entering the value for `a` a `'\n'` is inserted in the input stream. If you fail to account for the newline -- your next attempted read will read the `\n'`. This is also the primary reason that you are encouraged to take all user input with `fgets()` an a sufficiently sized buffer and then parse any needed information from the buffer using `sscanf()` instead. `fgets()` will consume the trailing `'\n'` – David C. Rankin Dec 18 '21 at 02:27
  • So any white space can signal to scanf to read? – mdjukan Dec 18 '21 at 02:30
  • The user's input is buffered by the terminal until the Enter key is pressed. That allows the user to edit the line before submitting it to your program. Once the user presses Enter, the whole line is copied to a buffer that the program controls. `scanf` reads from that buffer to fulfill any requests the program makes. When the buffer is empty, `scanf` will wait until the user presses Enter again. – user3386109 Dec 18 '21 at 02:31
  • Any whitespace in the `scanf()` *format-string* will cause leading whtespace before that conversion specifier to be discarded. The `scanf()` conversion specifiers that DO NOT discard leading whitespace are `"%c"`, `"%[..]"` and `"%n"`. All others discard leading whitespace. Pressing the **[Enter]** key triggers `scanf()` to read. User input is **line-buffered**. So until the **[Enter]** key is pressed, characters are simply placed into an internal read-buffer. – David C. Rankin Dec 18 '21 at 02:32
  • For Linux the internal read buffer is `8192` bytes specified by the `BUFSIZ` macro. See [glibc/libio/stdio.h - #define BUFSIZ 8192](https://sourceware.org/git/?p=glibc.git;a=blob;f=libio/stdio.h;=b63ee88a776aa12586f086ab49d05baadd12aeed;hb=refs/heads/master#l99) – David C. Rankin Dec 18 '21 at 02:36
  • What triggered second scanf to read? – mdjukan Dec 18 '21 at 02:36
  • Re-read the next to last comment by myself and the comment by @user3386109. – David C. Rankin Dec 18 '21 at 02:37
  • Note: when `scanf()` executes and when input arrives are asynchronous events. Users can type whenever they want. `scanf()` just says when code is interested in look at `stdin`. – chux - Reinstate Monica Dec 18 '21 at 03:37
  • You might find [this course notes chapter](https://www.eskimo.com/~scs/cclass/notes/sx6b.html) interesting, especially the paragraph beginning "Finally, don't be disappointed". That page is talking about `getchar`, but `scanf` does all of its input as if by calling `getchar`, so everything that's there applies, and in particular the fact that `scanf` has nothing to look at until the user hits the Enter key to terminate a line of input, at which point the whole line becomes available for `scanf` to chew on. – Steve Summit Dec 18 '21 at 04:43
  • Lexing code frequently (I'd even say typically) looks one character ahead (i.e., without "consuming" that character). Lexers (such as flex/re2c) work like that and reading numbers is essentially lexing interspersed with value-constructing code. IOW, the character that signals the end of a number does not need to be consumed. Implementing such one character lookahead is very easy with buffered input (and very much impossible with raw input (can't pushback characters back to the system nor peek at the kernel's internal buffers)). – Petr Skocik Dec 18 '21 at 14:44

1 Answers1

2

scanf works somewhat like a pattern-matching function. For example:

scanf("%d %f", &a, &b);

Would first look for an int starting from left, ignoring all whitespaces. If some unexpected character appeared before successful read, the functions returns immediately. Now, the final character in the string will be read and then put back for the next call of scanf to read. That is why we need to press the return key once more to expend the next call and nothing else is read. In order to work around this, we can pattern match the forthcoming scanf call as such:

scanf(" %c", &c1);
  • Note the space in front of the string. This indicates to scanf that one whitespace character is expected to be matched.

  • Important to note that scanf will discard ALL leading whitespace - no matter how many whitespace characters there are - thanks David C. Rankin

mindoverflow
  • 730
  • 4
  • 13
  • 3
    Almost - `"indicates to scanf that one whitespace character is expected to be matched"` - NO. It tells `scanf()` to discard ALL leading whitespace -- no matter how many whitespace characters there are. – David C. Rankin Dec 18 '21 at 02:40
  • I meant as in that particular example. – mindoverflow Dec 18 '21 at 02:41
  • 3
    More importantly, I'm not sure the OP understands the concept of [buffered I/O](https://stackoverflow.com/a/36758081/421195). scanf is "line oriented"- won't read ANYTHING until you hit "newline" (or unless you explicitly turn buffering off). Hence his confusion about "12345". – paulsm4 Dec 18 '21 at 02:41
  • 1
    You can correct with `"... at least one ..."`. Which would make it correct. From [man 3 scanf](https://man7.org/linux/man-pages/man3/scanf.3.html) *"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."* – David C. Rankin Dec 18 '21 at 02:43
  • Yes you make a good point. – mindoverflow Dec 18 '21 at 02:44
  • I don't know anything about buffered I/O. Where could I learn about it? – mdjukan Dec 18 '21 at 02:44
  • 1
    All it means (for line-buffered input) is when the user enters input, it isn't processed by `scanf()` as each character is typed. Instead, each character is placed in an internal read-buffer (that you have no control over) and when **[Enter]** is pressed, the entire line of input is passed to `scanf()` to be matched against the *format-string*. `scanf()` then returns the number of successful conversions that took place which tells you whether all variables you expected `scanf()` to fill -- were filled. – David C. Rankin Dec 18 '21 at 02:47
  • I don't think it is important to know too much about buffered I/O during the beginner stages. All that matters at this stage is that the newline char produced by the return key is read by scanf and put back such that it is read by a next call of scanf if there is one. – mindoverflow Dec 18 '21 at 02:48
  • @DavidC.Rankin please correct me if I'm wrong, but it is read by stdin buffer, right? – mindoverflow Dec 18 '21 at 02:49
  • 1
    It is buffered in a read-buffer and then made available as `stdin`. I would have to go look at the glibc source to determine what buffer is used (and I don't think that matters much), but you are correct that it is made available as `stdin` to the program. This area has changed a bit in the last few years. – David C. Rankin Dec 18 '21 at 02:52