The problem is that if you enter something like "sdfokhs" the first time, then scanf
will not be able to match any integer and will return 0. Because scanf
did not consume this invalid input from the input stream, calling scanf
a second time won't cause the user to be prompted for new input. Instead, scanf
will attempt again to match an integer from the non-consumed input, and will fail again for the same reason as the fist time. This means you have an infinite loop.
Therefore, to fix this, you must consume the rest of the line before calling scanf
again, for example like this:
while ( fgetc( stdin ) != '\n' ) ;
Or, if you want more robust error checking:
int c;
do
{
c = fgetc( stdin );
if ( c == EOF )
{
printf( "Unrecoverable error reading input!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
Also, it is always a good idea to check the return value of scanf
.
However, in this case, I don't recommend using scanf
. It would make more sense to always read exactly one line of input per loop iteration, using fgets
. The disadvantage of using scanf
is that it may read several lines of input per iteration, or only part of one line, which requires you to consume the rest of the line.
The following solution is longer than the solution of all other answers, but it is also the one with the most robust input validation and error handling.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_LINESIZE 100
void openMenu(int *op)
{
char buffer[MAX_LINESIZE];
char *p;
long converted;
//goto label
try_again:
//prompt user for input
printf( "Please enter number between 1 and 14: " );
//read line of input into buffer
if ( fgets( buffer, MAX_LINESIZE, stdin ) == NULL )
{
printf( "Unrecoverable error reading input!\n" );
exit( EXIT_FAILURE );
}
//make sure that a full line was read and remember position of newline character
p = strchr( buffer, '\n' );
if ( p == NULL )
{
int c;
printf( "Input was too long!\n" );
//attempt to consume input until newline character found
do
{
c = fgetc( stdin );
if ( c == EOF )
{
printf( "Unrecoverable error reading input!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
goto try_again;
}
//remove newline character from string
*p = '\0';
//convert string to number
converted = strtol( buffer, &p, 10 );
//make sure conversion was successful
if ( p == buffer )
{
printf( "Only enter a number!\n" );
goto try_again;
}
//verify that remainder of line is whitespace
while ( *p != '\0' )
{
if ( !isspace( (unsigned char)*p ) )
{
printf( "Only enter a number!\n" );
goto try_again;
}
p++;
}
//verify that number was in the correct range
if ( converted < 1 || converted > 14 )
{
printf( "Only enter a number between 1 and 14!\n" );
goto try_again;
}
//since all tests were passed, write the value
*op = converted;
}
Note that using goto
should normally not be done, if a loop can be used just as well. However, in this case, I believe it is the cleanest solution.