1

I am very new to C programming. My sir gave this code to find the maximum of n numbers. When I do as Sir says things are perfect i.e Write a number when the line - Type the number of numbers and write numbers in a row like 7 8 9 10 when Type the numbers pop up.

#include <stdio.h>
main()
{
    int n, max, number, i;
    printf("Type the number of numbers");
    scanf("%d", &n);
    if(n>0)
    {
        printf("Type the numbers");
        scanf("%d",&number);
        max=number;
        for(i=1; i<n; i++)
        {
            scanf("%d", &number);
            if(number>max)
                max=number;
        }
        printf("MAX=%d \n", max);
    }
}

But if I write suppose - 5 8 9 10 7 6 - then the program understands it like -- It puts n = 5 then puts number = 8 then loop executes number changes to 9 then number changes to 10 till 6 and then gves max.

So how is scanf working here? It takes digit individually although they are written in a row with spaces?

Ajay Brahmakshatriya
  • 8,993
  • 3
  • 26
  • 49
Ali Hasan
  • 149
  • 7
  • You might as well read the anual page for `scanf` - Google it EDIT Here is it https://linux.die.net/man/3/scanf – Ed Heal May 31 '17 at 16:07
  • Space and new-line are the same: it separates the numbers. You might try TAB, as well. They are called "white-space". – zdf May 31 '17 at 16:08
  • 1
    `%d` of `scanf` works as expected as it skips the previous white-space characters. – BLUEPIXY May 31 '17 at 16:09
  • 1
    The scanf family of functions does not deal in lines. A single call to scanf does not read a line. A single call to scanf may read part of a line, or it may read many lines. – Jonathan Leffler May 31 '17 at 16:23
  • Please don't tag as C++, unless your code is actually C++. C and C++ are very different languages these days. – tambre May 31 '17 at 16:37
  • " I write suppose - 5 8 9 10 7 6 - then the program understands it " --> What did you expect to happen? – chux - Reinstate Monica May 31 '17 at 16:40
  • The thing is that how the program understands the row of numbers as different numbers that too on the execution of different statements. It just not takes this as different integers, it executes a statement with a digit of the row & then on the execution of the next statement it takes the next digit from the same row. The way how the whitespace - space, tab is understood is confusing. I was expecting an error instead of an output. Please recommend a page, article or maybe a book to understand the behavior of scanf. – Ali Hasan May 31 '17 at 16:56
  • @AliHasan "Please recommend a page, " --> that was all ready provided on the first [comment](https://stackoverflow.com/questions/44289543/understanding-scanf-behaviour/44290563#comment75585977_44289543). IAC, SO is not a good place for general recommendations about learning a language, but good for specific problems. – chux - Reinstate Monica May 31 '17 at 17:05
  • @AliHasan To be clear: `scanf("%d",&number);` does **not** read a _line_ of user input. It reads white-space then integer numeric text until something non-numeric is found. – chux - Reinstate Monica May 31 '17 at 17:10
  • If you want to read about the `fscanf()` family of functions, [best to go to straight to the source](http://port70.net/~nsz/c/c11/n1570.html#7.21.6.2) and read about them in the Standard. If you need a good book about C basics, [here is a list of C books maintained on SO](https://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list). – ad absurdum May 31 '17 at 17:33

6 Answers6

1

The "%d" in scanf("%d",&number); causes has 3 stages of scanning user text input into an int.

  1. 0 or more leading whites-space, like ' ', '\n', '\t' and some others are read and discarded.

  2. Numeric text like "123", "-123", "+123" is read until until a non-numeric character is read. (or end-of-file, or a rare input error).

  3. That non-numeric character is put back into stdin for subsequent input calls.

If step 2 is successful in reading at least 1 digit, the function returns 1. Good code checks the returned value.

if (scanf("%d",&number) != 1) Handle_UnexpectedInput();

The important thing is that '\n' is not so special with scanf("%d",&number);. It acts like a separator like another white-space or non-numeric text.

'\n' does cause the buffered stdin to accept the line of user input for processing by the various scanf() calls.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

Here it is simplified explanation (not overly simplified, I hope) on how scanf works from a user point of view:

The arguments are divided in two parts:

  1. The first part is a “format string”. The string is made of at least one format specifier. In its simplest form a specifier begins with % and it is followed by a letter that specifies the type of variable you’re expecting (“%d” – I’m expecting an integer). The number of specifiers must match the number of parameters and types in the second part.

  2. The second part is made of one or more addresses to locations memory where the data you input will be stored. The pointed types must match the specifiers.

When called, the function will repeat the following steps, starting with the first specifier and the first pointer, until the end of format string is detected:

  1. Read and discard any white-space until a non-white-space character is found (white-space: space, tab, NL, at least);
  2. Read characters up to first white-space or a character that do not match the expected input for current specifier;
  3. Convert them to the type of current specifier and
  4. Store the result in the location pointed by the current pointer.

There are three typical beginner mistakes which will result in undefined behavior (crash, most likely):

  1. You forget the address-of operator &.
  2. The specifier and the type do not match.
  3. The number of specifiers do not match the number of pointers.

  int d;
  scanf( "%d", d ); // no &
  scanf_s( "%s", &d ); // s do not match int
  scanf_s( "%d%d", &d ); // too many specifiers
zdf
  • 4,382
  • 3
  • 18
  • 29
  • 1
    At the risk of complicating things, initial whitespace characters are not skipped over for the `%c`, `%n`, or `%[]` specifiers; this sometimes causes beginners trouble when a newline has been left behind in the input stream. Also, the first non-matching character is left in the input stream, often leading to problems for later calls to I/O functions. – ad absurdum May 31 '17 at 17:41
  • @DavidBowling You're right but I am afraid that I have already made things complicated. – zdf May 31 '17 at 17:43
1

From the horse's mouth:

7.21.6.2 The fscanf function
...
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 the isspace function) are skipped, unless the specification includes a [, c, or n specifier.284)

9    An input item is read from the stream, unless the specification includes an n 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.

10    Except in the case of a % specifier, the input item (or, in the case of a %n directive, the count of input characters) is converted to a type appropriate to the conversion specifier. If the input item is not a matching sequence, the execution of the directive fails: this condition is a matching failure. Unless assignment suppression was indicated by a *, the result of the conversion is placed in the object pointed to by the first argument following the format argument that has not already received a conversion result. If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.
...
12    The conversion specifiers and their meanings are:

        d    Matches an optionally signed decimal integer, whose format is the same as
              expected for the subject sequence of the strtol function with the value 10 for
              the base argument. The corresponding argument shall be a pointer to signed integer.
...
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.

The processing for scanf is exactly the same; the only difference is that scanf always reads from standard input.

Examples:

Suppose you type SpaceSpaceSpace123Enter in response to the first prompt; the input stream then contains the sequence {' ', ' ', ' ', '1', '2', '3', '\n'}. When you call scanf( "%d", &n );, scanf reads and discards the leading blank spaces, then reads and matches the sequence {'1', '2', '3'}, converts it to the integer value 123, and assigns the result to n. Since there was a successful conversion and assignment, scanf returns 1.

If the input stream contains the sequence {' ', ' ', ' ', '1', '2', '.', '3', '\n'}, scanf reads and discards the leading blanks, then reads and matches the sequence {'1', '2'}, converts it to the integer value 12, and assigns the result to n. The input stream will still contain {'.', '3', '\n'}. Since there was a successful conversion and assignment, scanf will return 1.

If the input stream contains the sequence {'.', '3', '\n'}, then there is no matching sequence of characters ('.' is not a valid character in a decimal integer). scanf will leave the . unread and leave the value of n unchanged. Since there was not a successful conversion and assignment, scanf returns 0 to indicate a matching failure.

If an end-of-file is signaled on the input stream before any matching characters have been read, or if there's some other input error, scanf does not assign any new value to n and returns EOF to indicate an input failure.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

when you press the keyboard, you are filling a buffer on your computer. the scanf will read the amount of consecutive data untill it hit a space, so "1234 43", on the code scanf("%d") you are saying "read one number", and 1234 is one number, that's what it will read.

But if you have a loop that will execute that scanf again, the number "43" is currently in the reading buffer, and scanf will read it without stopping.

The manual for scanf doesn't explains that and it's a bit confusing for a newcomer to understand why the application is not stopping there to read a new number.

Tomaz Canabrava
  • 2,320
  • 15
  • 20
0

Let me explain as simply as possible...

scanf reads bytes from the standard input stream stdin.

Let's say the input you give is "23 67 21 99\n" (The \n is from when you pressed Enter).

Then each next call to scanf will start reading from this input buffer and it will interpret what is sees as whatever you tell it ("%d", etc) while separating inputs by an empty character. This could be a new line, a space, a tab, etc.

While there are still bytes to be read, scanf will not wait for you to input. That is what is happening here.

DeiDei
  • 10,205
  • 6
  • 55
  • 80
0

let's keep it simple. I assume you don't know anything about buffer or stdin. scanf is used to take input from user. Whenever you type a number and press 'space' or ' enter' on keyboard the number is entered into program for further purposes. When you type scanf("%d",&n); it mean take integer input from the user and store it on the address of variable n.

  • This was helpful. But the way how the program goes back to the row of integers (with spaces) as program further executes is confusing. Like from the row 2 3 4 5 6 7 8 it will first take the digit 2, run a statement then for next statement it will take 3 from user execute it and then for next statement it will take 4 and execute it and so on... What my issue is why not an error. I may sound ridiculous. Can you please recommend me a book for basics of C to clear doubts like this. The behavior of scanf. – Ali Hasan May 31 '17 at 17:04
  • **for(i=1; imax)** // if that number is greater than max, **max=number;** /*then, max is now same as that number... But if number is less than max, if will not execute and max will still be the previously stored number. */ since this is in the loop, after this again the scanf will be called and all this code in this {...} will repeat. Scanf doesn't read the numbers from beginning everytime, the magic is in 'if' condition. } –  May 31 '17 at 17:15