0

I have a following code in C

#include <stdio.h>
int main()
{
    int number;

    // printf() displays the formatted output
    printf("Enter an integer: ");

    // scanf() reads the formatted input and stores them
    scanf("%d", &number);

    // printf() displays the formatted output
    printf("You entered: %d", number);
    return 0;
}

Here I am expected to enter a number but I have entered a character 'a' and it results 29. I expected that it will throw an exception but I was surprised by an output. Screen shots of an output of the program.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Albert Einstein
  • 7,472
  • 8
  • 36
  • 71
  • 5
    Where did you read that C had exceptions? Check the return value of functions that can fail – Mat Dec 25 '17 at 22:15
  • 1
    Your documentation for `scanf` should tell you what happens on invalid input. It won't mention exceptions there. –  Dec 25 '17 at 22:17
  • You entered `a` which was refused by the `%d` format spec, which needs a decimal text number. So because `int number` is uninitialised, you got an arbitrary answer. – Weather Vane Dec 25 '17 at 22:21
  • 1
    I recommend you [read a good `scanf` (and family) reference](http://en.cppreference.com/w/c/io/fscanf), and check what `scanf` ***returns***. – Some programmer dude Dec 25 '17 at 22:23
  • 1
    And pay close attention to the phrase *matching failure* in the documentation and what `scanf` does (or doesn't do) with the input that remains in `stdin` when the failure occurs. – David C. Rankin Dec 25 '17 at 22:27
  • 2
    *"I expected that it will throw an exception but I was surprised by an output."* -- When coding, don't assume things with functions that you don't understand. Read the documentation instead. If you have problems with the documentation, then you can ask about those problems. Otherwise you are just wasting your time. – Pablo Dec 25 '17 at 22:30
  • [Reading a good beginners book or two](https://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list) would also be useful, instead of guessing about language features. – Some programmer dude Dec 25 '17 at 22:31

4 Answers4

3

For starters the function main without parameters shall be declared like

int main( void )

Just change your program the following way

#include <stdio.h>

int main( void )
{
    int number;

    // printf() displays the formatted output
    printf("Enter an integer: ");

    // scanf() reads the formatted input and stores them
    if (scanf("%d", &number) == 1)
    {
        // printf() displays the formatted output
        printf("You entered: %d\n", number);
    }
    else
    {
        puts("Invalid input");
    }

    return 0;
}

and see what will happen if you try to enter the character 'a' instead of a number.

According to the description of the function in the C Standard (7.21.6.2 The fscanf function)

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.

And (7.22.1.4 The strtol, strtoll, strtoul, and strtoull functions)

7 If the subject sequence is empty or does not have the expected form, no conversion is performed; the value of nptr is stored in the object pointed to by endptr, provided that endptr is not a null pointer.

Thus in the demonstrative program the else statement will be executed because (The C Standard, 7.21.6.2 The fscanf function)

16 The fscanf function returns the value of the macro EOF if an input failure occurs before the first conversion (if any) has completed. Otherwise, the function returns the number of input items assigned, which can be fewer than provided for, or even zero, in the event of an early matching failure

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
2

You have fallen into the same trap that snares virtually all new C programmers that do not take the time to fully understand the scanf family of function before attempting to take user input with it.

Before getting to the specifics, any time you take user input, you must validate the input. That means checking the return for whatever function you used to validate input was in fact received, and if necessary, validate that the input received was within the expected/acceptable range of values for your code. Using scanf is no different.

The preferred method for user input is fgets because it avoids the pitfalls inherent in scanf and completely consumes each line of input (provided the buffer is large enough, or if it is called repeatedly until you validate the last character read was a '\n')

With scanf, it is up to you to account for, and remove, any characters that remain in stdin in the event of (1) successful input or (2) an input or matching failure where additional attempts at input will follow.

To safely use scanf for user input, you have three conditions to test for, (1) did the return indicate all conversions specified in the format string took place? If so, empty stdin and check the range of values received as required. (2) did the user cancel input by pressing Ctrl+d (or Ctrl+z on windoze). If so, gracefully exit insuring a final '\n' is provided in your output -- (OS & compiler dependent); and finally (3) did a matching or input failure occur? If so, handle the error and empty any invalid characters from stdin.

When you are taking user input, an invalid input does you no good. So you generally want to loop until you receive valid input or the user cancels, that can be done relatively easily with a simple infinite loop, e.g.

/** empty all characters reamining in stdin */
void empty_stdin()
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}
...

    for (;;) {          /* loop until valid input or user cancels */
        int rtn = 0;    /* variable to capture scanf return */

        /* printf() displays the formatted output */
        printf ("Enter an integer: ");

        /* scanf() reads the formatted input and stores them */
        rtn = scanf ("%d", &number);

        if (rtn == 1) {         /* we got an integer value */
            empty_stdin();      /* empty any remaining characters */
            break;              /* continue with program */
        }
        else if (rtn == EOF) {  /* user canceled with ctrl+d (ctrl+z on win) */
            fprintf (stderr, "user canceled input.\n");
            return 1;
        }
        /* handle input or matching failure */
        fprintf (stderr, "error: matching or input failure occurred.\n");
        empty_stdin();          /* empty any remaining characters */
    }

Stepping though the input loop, you will notice each of the three conditions you must check for are handled, and any characters that remain in stdin are emptied in preparation for the next input. Your complete code simply outputs the result of the successful input (and if on windows, adds a final getchar(); to prevent the terminal window from closing if you are running withing an IDE, e.g.

#include <stdio.h>

/** empty all characters reamining in stdin */
void empty_stdin()
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

int main (void) {

    int number = 0;

    for (;;) {          /* loop until valid input or user cancels */
        int rtn = 0;    /* variable to capture scanf return */

        /* printf() displays the formatted output */
        printf ("Enter an integer: ");

        /* scanf() reads the formatted input and stores them */
        rtn = scanf ("%d", &number);

        if (rtn == 1) {         /* we got an integer value */
            empty_stdin();      /* empty any remaining characters */
            break;              /* continue with program */
        }
        else if (rtn == EOF) {  /* user canceled with ctrl+d (ctrl+z on win) */
            fprintf (stderr, "user canceled input.\n");
            return 1;
        }
        /* handle input or matching failure */
        fprintf (stderr, "error: matching or input failure occurred.\n");
        empty_stdin();          /* empty any remaining characters */
    }

    /* printf() displays the formatted output */
    printf ("You entered: %d\n", number);

#if defined (_WIN32) || defined (_WIN64)
    getchar ();     /* to hold terminal open -- don't use getch() */
#endif

    return 0;
}

Example Use/Output

$ ./bin/scanf_int
Enter an integer: apples
error: matching or input failure occurred.
Enter an integer: a 10 b 50 c 90
error: matching or input failure occurred.
Enter an integer: 12345aaa
You entered: 12345

Example where input canceled. (note: on Win10, ctrl+z does not generate EOF by default, See CTRL+Z does not generate EOF in Windows 10 | PROC-X.com)

$ ./bin/scanf_int
Enter an integer: NO!
error: matching or input failure occurred.
Enter an integer: user canceled input.

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
1

I think "scanf()" doesn't read "a" as as number and simply skips to put any number in "number" and 29 is the initial value of the number which is found in stack!

try to initialize "number" with something like 123 and you will get 123 in output.

jmowla
  • 166
  • 1
  • 4
1

%d expects to see one or more decimal digits in the input stream; ’a’ is not a decimal digit, so you get a matching failure - number is not updated, a is left in the input stream, and scanf returns 0 to indicate that no conversion and assignment occurred.

Since you don’t initialize number when you declare it, it contains an indeterminate value, which in this case was 29.

C doesn’t use exceptions for unexpected input - you have to check the return value of scanf.

int result = scanf( “%d”, &number );
if ( result == 1 )
{
  // user entered one or more decimal digits for number
}
else if ( result == 0 )
{
  // user entered something that isn’t a decimal digit
}
else // result == EOF
{
  // EOF or error detected on input
}
John Bode
  • 119,563
  • 19
  • 122
  • 198