The functions gets
and fgets
are used for reading a line of input from the user as a string. In contrast to scanf
, it cannot read in a number directly. If you want to convert the string that it reads to a number, then you must do that in a separate step, for example by using the function strtol
.
Although the simplest solution to read a number from the user is to use scanf
with the %d
format specifier, I don't recommend doing that, as that function does crazy things, because it is not designed for line-based input. For example, when the user enters "6sdfh4q"
, scanf
accepts the "6"
as valid input, but subsequent calls to scanf
will fail, unless you remove the remainder of the line ("sdfh4q"
) from the input stream manually.
See this guide for more information on why not to use scanf
:
A beginners' guide away from scanf()
The function gets
was removed from the ISO C standard, because it was too dangerous to use. Therefore, for reading a string from the user, I recommend that you use the function fgets
instead. Afterwards, you can use the function strtol
to convert that string to a number, for example like this:
//the code below requires the following headers
#include <stdio.h>
#include <stdlib.h>
[...]
char buffer[100], *p;
long l;
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "error reading input\n" );
}
else
{
//attempt to convert string to number
l = strtol( buffer, &p, 10 );
if ( p == buffer )
{
printf( "error converting string to number\n" );
}
else
{
//do something with the converted number
}
}
However, this code has the following problems:
It does not check whether the input buffer was large enough to contain the whole line of input.
It does not check whether the number the user entered is so high or low that it is not representable as a long
.
The function strtol
provided a long
as the converted number, so additional range checks will be required if you want an int
. You will have to make sure that the number is not too high or too low to be representable as an int
.
If the user enters "6sdfh4q"
, it will consider this valid input of the number 6
, because the first character of the line is a digit. It will ignore the rest of the line, although the entire input should probably be rejected in this case. It would probably be best to reject the input if it contains any non-whitespace characters after the converted digits.
The following code solves all of these problems.
In this code, I provide a function get_int_from_user
with a simple interface. It takes one parameter which points to the string that the user should be prompted with (e.g. "Please enter a number: "
). The function will repeat this prompt until the user enters a valid number. Once the user has entered a valid number, it will return that value as an int
(provided that the number is not too high or low to be representable as an int
).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
int get_int_from_user( const char *prompt )
{
for (;;) //loop forever until user enters a valid number
{
char buffer[1024], *p;
long l;
fputs( prompt, stdout );
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "unrecoverable error reading from input\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 "6sdfh4q" 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 next_outer_loop_iteration;
}
}
return l;
next_outer_loop_iteration:
continue;
}
}
Instead of writing
printf ("Enter the testing number\n") ;
scanf ("%d", &n);
you can call the function get_int_from_user
like this:
n = get_int_from_user( "Enter the testing number: " );