-1

I wanted to store n values given (n was known) in an array but I didn't know how to do it, so I looked and found this post: C, reading multiple numbers from single input line (scanf?) , and this answer:

int main()
{
    int i, size, *v;
    scanf("%d", &size);
    v = malloc(size * sizeof(int));
    for (i=0; i<size; i++)
        scanf("%d", &v[i]);
    printf("%d %d %d", v[0], v[1], v[2]);
}

I don't understand why scanf can do that, shouldn't the function, in every iteration of the loop, ask for another input? How can it read one int, then another and so on, as long as they are separated by spaces? How can scanf behave that way? Because it seems like it shouldn't.

PS: I only wrote the final print statement with 3 values for simplicity, in "my case" size was 3.

Thank you in advance!

Stephen Docy
  • 4,738
  • 7
  • 18
  • 31
StuKers
  • 21
  • 4
  • Why not? Lines only mean something to you, not the computer. It just sees bytes. – stark Jan 30 '18 at 22:24
  • 1
    I don't understand your question. What exactly is what you expect `scanf` to do? – Pablo Jan 30 '18 at 22:24
  • SCANF never ask for inputs, that’s the programmers Job. – Michi Jan 30 '18 at 22:26
  • I expect scanf to ask for an int in every iteration of the loop, but apparently if you give it an input with 3 ints, when size=3, separated by a space it only asks for input the first time. – StuKers Jan 30 '18 at 22:30
  • 1
    scanf reads stdin, period. So either what you are typing at the console or the file contents that are piped to stdin. It never 'asks' , if there is nothing to read it will just wait (interactive) or fail (from file) – pm100 Jan 30 '18 at 22:36
  • @pm100 thank you. I though scanf really asked for an input, but it just reads what's given to the console, from what I understood. So in this case it is waiting to read "size" values, but if we write those values all at once, scanf can "get" them all at once too. Thank you. – StuKers Jan 30 '18 at 22:49
  • Any input reading function (like `scanf`or `fgets`) tries first to consume input from the input buffer. If there is nothing to consume, it will read from the stream. The case of `stdin` because it closes only when the program exits (unless you close it yourself) or when the pipe is closed. That's why the call blocks (on `stdin`) if there is nothing in the input buffer until the user enters something in the console, thus adding characters to the stream. – Pablo Jan 30 '18 at 23:04
  • 2
    `scanf()` does not read per a _line_ of input, but the _stream_. – chux - Reinstate Monica Jan 30 '18 at 23:49

3 Answers3

5

When you type in some numbers on your console and hit ENTER, you are providing an input stream to your code via stdin. The entire contents of what you type in are kept in that input stream. So if you type

2 4 6 8

and hit enter, the input stream contains:2 4 6 8

Now, each of your scanfs is only reading (and removing) a single integer from that input stream, so after a single scanf, the stdin input stream now contains: 4 6 8

The extra input is not discarded, it is still available in the input stream, so your next scanf can come along and grab the next integer from the stream, and the next, and the next.....until the data in the input stream is exhausted.

Stephen Docy
  • 4,738
  • 7
  • 18
  • 31
  • 2
    The newline character will also be part of the input stream. This is important specially for conversion specifiers like like `%s`, because the newline will remain in the input buffer. – Pablo Jan 30 '18 at 22:59
  • 1
    the input stream contains:`"2 4 6 8\n"` – chux - Reinstate Monica Jan 30 '18 at 23:50
  • 1
    @chux -- if everyone remembered that the input stream contains newlines, there might only be [135,000 questions under the C tag](https://stackoverflow.com/questions/tagged/c) – ad absurdum Jan 31 '18 at 00:19
5

This is one of the surprising things about scanf.

If I write

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

it looks like I'm expecting to read a line with three numbers on it. But if what the user actually types is

1
2
3

(that is, on three separate lines), it'll "work" just fine. scanf wanted three integers, and it found them, but it had to read three lines in order to do so.

Contrariwise, if I wrote

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

and the user typed

4 5 6

on one line, again, this'll "work" just fine, there were three scanf calls asking for a total of three ints, and there were three ints there -- although they happened to be on one line.

And in your case, your scanf call in a loop has about the same effect as the several separate scanf calls.

The bottom line is that scanf is not line-based. There is no one-to-one correspondence between calls to scanf and expected lines of input. scanf does not treat newline characters specially -- they're just treated as whitespace, separating other input fields.

(By way of comparison, printf isn't line-based, either, although it does at least treat \n characters specially. But there's no different in output between

printf("one ");
printf("two ");
printf("three\n");

and

printf("one two three\n");

.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
2

After reading the original question, I think I understand what you are asking.

So in the other question the input was:

5<ENTER>
1 2 3 4 99<ENTER>

scanf is a function design to read formatted input (the name comes from scan formatted), it is not design to read random user input.

To quote the man page of scanf:

man scanf

#include <stdio.h>

int scanf(const char *format, ...);

DESCRIPTION

The scanf() family of functions scans input according to format as described below. This format may contain conversion specifications; the results from such conversions, if any, are stored in the locations pointed to by the pointer arguments that follow format. Each pointer argument must be of a type that is appropriate for the value returned by the corresponding conversion specification.

That means that scanf scans the input according to the provided format. The conversion specifier %d matches an optionally signed decimal integer. If your format contains only "%d" it will consume any white-space characters and try to convert the next sequence of non-white-space characters into an signed int. If this is possible, scanf will store the converted integer in the memory pointed to by the passed argument. If it fails at converting, it stops scanning and any other character in the input buffer will remain in the input buffer.

So let's to this: You enter 3 numbers like this:
3SPACESPACE4SPACETAB5ENTER

Your input buffer will look like this:

+---+---+---+---+---+----+---+----+
| 3 | █ | █ | 4 | █ | \t | 5 | \n |
+---+---+---+---+---+----+---+----+

In the first iteration scanf consume any white-space characters, right now there is none, so it will read 3 and continue with the next character which is a space. At that point it stops and converts the 3 and stores it in &v[i]. The input buffer will look like this:

+---+---+---+---+----+---+----+
| █ | █ | 4 | █ | \t | 5 | \n |
+---+---+---+---+----+---+----+

In the second iteration it will consume any white-space characters. There are two spaces there and those are just consumed. Then it will read 4, and then a white-space character. scanf stops and and converts the 4 and stores it in &v[i]. The input buffer will look like this:

+---+----+---+----+
| █ | \t | 5 | \n |
+---+----+---+----+

As you can see, in every iteration scanf didn't wait block and wait for the user to enter something, because characters were left in the input buffer from the previous iterations.

And that's why you can have scanf("%d", &v[i]) in a loop for an input like this "1 2 3 4 5".

Wyck
  • 10,311
  • 6
  • 39
  • 60
Pablo
  • 13,271
  • 4
  • 39
  • 59