You should always check the return value of scanf
to determine whether the input was successfully converted, before you attempt to use the result.
However, for line-based user input, using the function scanf
is generally not advisable, because its behavior is not intuitive. It usually does not consume an entire line of input, which can cause all kinds of trouble. For example, scanf
leaving the newline character on the input stream can cause this problem. If the user enters 6abc\n
, then the situation is even worse: In that case, scanf
will match the 6
as valid input, but leave abc\n
on the input stream, which will most likely cause the next input operation to not behave as intended, unless you explicitly discard the remainder of the line from the input stream beforehand.
For the reasons stated above, it is generally better to always read an entire line of input at once as a string, for example using the function fgets
. You can then use the function strtof
to attempt to convert the string to a number.
Here is an example, which performs extensive input validation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
float get_float_from_user( const char *prompt );
int main( void )
{
float monthpay;
//loop forever until user enters a valid number
for (;;) //infinite loop, equivalent to while(1)
{
monthpay = get_float_from_user( "Please enter your monthly wage: ");
if ( monthpay > 0 )
{
//input is ok, so we can break out of the loop
break;
}
printf( "Please enter a positive number!\n" );
}
printf( "Input was valid. You entered: %.2f\n", monthpay );
return 0;
}
float get_float_from_user( const char *prompt )
{
//loop forever until user enters a valid number
for (;;)
{
char buffer[1024], *p;
float f;
//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;
f = strtof( buffer, &p );
if ( p == buffer )
{
printf( "Error converting string to number!\n" );
continue;
}
//make sure that number is representable as a "float"
if ( errno == ERANGE )
{
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 f;
continue_outer_loop:
continue;
}
}
This program has the following behavior:
Please enter your monthly wage: abc
Error converting string to number!
Please enter your monthly wage: 6abc
Unexpected input encountered!
Please enter your monthly wage: 6e+2000
Number out of range error!
Please enter your monthly wage: -5
Please enter a positive number!
Please enter your monthly wage: 0
Please enter a positive number!
Please enter your monthly wage: 6.3
Input was valid. You entered: 6.30
The function get_float_from_user
that I am using in the code above is based on the function get_int_from_user
from this answer of mine to another question. See that answer for a further explanation on how that function works.