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.