Doing something like
while(age == (put something here)) {
would not make sense, because if scanf
fails due to the user entering invalid input, then the variable age
will have an indeterminate value.
Instead of checking the value of age
, you should check the return value of scanf
to determine whether the conversion was successful, for example like this:
#include <stdio.h>
int main( void )
{
int age;
printf( "Enter your age: " );
if ( scanf( "%d", &age ) == 1 )
{
printf( "Input was valid! It was %d.\n", age );
}
else
{
printf( "Input was invalid!\n" );
}
}
If you want to do this in a loop which repeats until the input is valid, then you could use this code:
#include <stdio.h>
int main( void )
{
int age;
//repeat until input is valid
for (;;) //infinite loop, equivalent to while(1)
{
printf( "Enter your age: " );
if ( scanf( "%d", &age ) != 1 )
{
int c;
printf( "Invalid input! Try again.\n" );
//discard remainder of line
do
{
c = getchar();
} while ( c != EOF && c != '\n' );
continue;
}
//input was ok, so break out of the infinite loop
break;
}
printf( "The input was successfully converted to %d.\n", age );
}
This program has the following behavior:
Enter your age: abc
Invalid input! Try again.
Enter your age: 25
The input was successfully converted to 25.
However, this solution is not perfect, as it accepts the input 25abc
as valid input for the number 25
:
Enter your age: 25abc
The input was successfully converted to 25.
In this case, it would probably be better to instead reject the input and to prompt the user to enter new input.
Another issue is that scanf
will not print an error message and will not reprompt the user if the user enters an empty line, but will instead continue reading input until a non-whitespace character is entered:
Enter your age:
abc
Invalid input! Try again.
Enter your age:
25
The input was successfully converted to 25.
This is because scanf
will consume and discard all whitespace characters when the %d
specifier is used.
When dealing with line-based user input, it is usually better not to use scanf
. Instead, I recommend to always read one entire line of input as a string using the function fgets
and then to attempt to convert that string to an integer using the functon strtol
.
Here is an example program which fixes all of the issues mentioned above, and adds some additional input validation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
//forward declaration
int get_int_from_user( const char *prompt );
int main( void )
{
int age;
age = get_int_from_user( "Enter your age: " );
printf( "The input was successfully converted to %d.\n", age );
}
//This function will attempt to read one integer from the user. If
//the input is invalid, it will automatically reprompt the user,
//until the input is valid.
int get_int_from_user( const char *prompt )
{
//loop forever until user enters a valid number
for (;;)
{
char buffer[1024], *p;
long l;
//prompt user for input
fputs( prompt, stdout );
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "Unrecoverable input error!\n" );
exit( EXIT_FAILURE );
}
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
{
int c;
printf( "Line input was too long!\n" );
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "Unrecoverable error reading from input!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
continue;
}
//attempt to convert string to number
errno = 0;
l = strtol( buffer, &p, 10 );
if ( p == buffer )
{
printf( "Error converting string to number!\n" );
continue;
}
//make sure that number is representable as an "int"
if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
{
printf( "Number out of range error!\n" );
continue;
}
//make sure that remainder of line contains only whitespace,
//so that input such as "6abc" gets rejected
for ( ; *p != '\0'; p++ )
{
if ( !isspace( (unsigned char)*p ) )
{
printf( "Unexpected input encountered!\n" );
//cannot use `continue` here, because that would go to
//the next iteration of the innermost loop, but we
//want to go to the next iteration of the outer loop
goto continue_outer_loop;
}
}
return l;
continue_outer_loop:
continue;
}
}
This program has the following behavior:
Enter your age:
Error converting string to number!
Enter your age: test
Error converting string to number!
Enter your age: 25abc
Unexpected input encountered!
Enter your age: 25
The input was successfully converted to 25.