You cannot use scanf
with the %d
conversion format specifier to read a character.
Although it is possible to solve this problem with scanf
, I instead recommend that you read an entire line of input as a string with the function fgets
. If you determine that the user did not enter "q"
, then you can attempt to convert that string to an integer, and then check whether this integer is in the range 1
to 9
.
Here is an example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//forward declaration
void get_line_from_user( char *buffer, int buffer_size );
int main( void )
{
char board[9] = {
'C', ' ', 'U',
'U', ' ', ' ',
'C', 'U', ' '
};
long choice;
//repeat until input is valid
for (;;) //infinite loop, equivalent to while(1)
{
char line[200], *p;
printf( "Enter a square (1-9) or \"q\" to quit: " );
get_line_from_user( line, sizeof line );
//determine whether user wants to quit
if ( strcmp( line, "q" ) == 0 )
{
printf( "Quitting program!\n" );
exit( EXIT_SUCCESS );
}
//user did not want to quit, so attempt to convert input to
//an integer
choice = strtol( line, &p, 10 );
if ( p == line )
{
printf( "Unable to convert input to an integer!\n" );
continue;
}
//verify that the remainder of the line does not contain any
//non-whitespace characters, so that input such as "6abc"
//gets rejected
for ( ; *p != '\0'; p++ )
{
if ( !isspace( (unsigned char)*p ) )
{
printf( "Unexpected character encountered!\n" );
//we cannot use "continue" here, because this would
//continue the innermost loop, but we want to continue
//the outer loop
goto continue_outer_loop;
}
}
//verify that input is in the desired range
if ( choice < 1 || choice > 9 )
{
printf( "Please enter a number between 1 and 9!\n" );
continue;
}
//verify that square is not already occupied
if ( board[choice-1] == 'C' || board[choice-1] == 'U' )
{
printf( "Board square is already occupied!\n" );
continue;
}
//input is ok, so we can break out of the infinite loop
break;
continue_outer_loop:
continue;
}
printf( "Input is valid!\n" );
printf( "You entered: %ld\n", choice );
}
//This function will read exactly one line of input from the
//user. If the line is too long to fit in the buffer, then the
//function will automatically reprompt the user for input. On
//failure, the function will never return, but will print an
//error message and call "exit" instead.
void get_line_from_user( char *buffer, int buffer_size )
{
char *p;
//attempt to read one line of input
if ( fgets( buffer, buffer_size, stdin ) == NULL )
{
printf( "Error reading from input\n" );
exit( EXIT_FAILURE );
}
//attempt to find newline character
p = strchr( buffer, '\n' );
//make sure that entire line was read in (i.e. that
//the buffer was not too small to store the entire line)
if ( p == NULL )
{
//attempt to read one more character
int c = getchar();
//a missing newline character is ok if the next
//character is a newline character or if we have
//reached end-of-file (for example if the input is
//being piped from a file or if the user enters
//end-of-file in the terminal itself)
if ( c != '\n' && !feof(stdin) )
{
printf( "Input was too long to fit in buffer!\n" );
//discard remainder of line
while ( c != EOF && c != '\n' )
{
c = getchar();
}
}
}
else
{
//remove newline character by overwriting it with
//null character
*p = '\0';
}
}
This program has the following behavior:
Enter a square (1-9) or "q" to quit: test
Unable to convert input to an integer!
Enter a square (1-9) or "q" to quit: 6abc
Unexpected character encountered!
Enter a square (1-9) or "q" to quit: -5
Please enter a number between 1 and 9!
Enter a square (1-9) or "q" to quit: 0
Please enter a number between 1 and 9!
Enter a square (1-9) or "q" to quit: 10
Please enter a number between 1 and 9!
Enter a square (1-9) or "q" to quit: 1
Board square is already occupied!
Enter a square (1-9) or "q" to quit: 4
Board square is already occupied!
Enter a square (1-9) or "q" to quit: 5
Input is valid!
You entered: 5