#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
int num, count = 0, ans;
srand(time(0));
ans = rand() % 1000;
printf("Im thinking about a three digit number.\nCan you guess it??\n");
do {
printf("\nENTER YOUR GUESS:\n");
scanf("%d", &num);
if (num < ans) {
printf("Enter a higher number!!!\n");
} else if (num > ans) {
printf("Enter a lower number!!!\n");
} else if (num == ans) {
printf("\n\t =================\n");
printf("\t|| CONGRATULATIONS!! ||\n\t =================\n\nYOU HAVE GUESSED THE CORRECT NUMBER.\n");
} else {
printf("ENTER A VALID 3 DIGIT NUMBER!!!");
}
count ;
} while(num != ans);
printf("You took %d chances to answer!\n", count);
return 0;
}
When the user enters characters other than digit such as letters or symbols it goes into printing Enter a lower number!!!
indefinitely. Please can anyone tell how to avoid it. Also the last else
statement is supposedly redundant and cannot be reached. Is there anyway to make this?
CodePudding user response:
You can and should detect invalid input by testing the return value of scanf()
. Offending input can then be discarded using a simple loop.
Regarding the redundant else
, the redundancy is in the test if (num == ans)
: remove this test and the final else
clause.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
int num, c, count = 0, ans;
srand(time(0));
ans = rand() % 1000;
printf("Im thinking about a three digit number.\nCan you guess it??\n");
for (;;) {
printf("\nENTER YOUR GUESS:\n");
if (scanf("%d", &num) != 1) {
printf("invalid input\n");
/* discard the pending input line */
while ((c = getchar()) != EOF && c != '\n')
continue;
if (c == EOF) {
printf("premature end of file\n");
break;
}
continue;
}
if (num == ans) {
printf("\n\t =================\n");
printf("\t|| CONGRATULATIONS!! ||\n"
"\t =================\n\n"
"YOU HAVE GUESSED THE CORRECT NUMBER.\n");
break;
}
if (num < ans) {
printf("Enter a higher number!!!\n");
} else {
printf("Enter a lower number!!!\n");
}
count ;
}
printf("You took %d chances to answer!\n", count);
return 0;
}
CodePudding user response:
You are getting an infinite loop because when the user does not enter a number, scanf
will fail without consuming any input. Therefore, the next time you call scanf
, it will read the exact same input again and fail again for exactly the same reason.
Therefore, after using scanf
with line-based user input, unless you want to do something with the rest of the line, you should always discard the remainder of the line, by consuming all characters on the input stream up to an including the newline character. You can do this for example by defining and calling the following function:
void discard_remainder_of_line( void )
{
int c;
do
{
c = getchar();
} while ( c != '\n' && c != EOF );
}
A shorter way of accomplishing the same thing would be to write:
for ( int c; (c = getchar()) != '\n' && c != EOF; )
;
Also, before using the result of a scanf
operation, you should always check the return value of scanf
, in order to verify that it was successful.
However, I don't recommend that you use scanf
for line-based user input, because when dealing with such input, scanf
does not behave in an intuitive manner. For example, as pointed out above, it does not always read one line at once.
Therefore, I recommend that you use the function fgets
instead, which always reads one line at once, provided that the memory buffer is large enough to store the entire line. After reading in a whole line as a string using fgets
, you can convert the string to a number, by using the function strtol.
It would probably be best to create a function get_int_from_user
which prompts the user to input a number, and if the input is not valid, prints an error message and reprompts the user for input. As soon as the input is valid, it returns the input as an int
. In the program below, I call this function get_int_from_user
.
That way, all you have to do to make your code work is to write this function get_int_from_user
and to change the lines
printf("\nENTER YOUR GUESS:\n");
scanf("%d", &num);
to:
num = get_int_from_user( "Enter your guess: " );
Also, in your code, the line else {printf("ENTER A VALID 3 DIGIT NUMBER!!!");}
is redundant, because that code is unreachable. That code will only be reached when num < ans
is false and num > ans
is false and num == ans
is false. However, it is not possible for all three conditions to be false.
Performing the check num == ans
is also redundant, because if num < ans
is false and num > ans
is false, then num == ans
must be true.
After making the changes described above, your program should look like this:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
int get_int_from_user( const char *prompt );
int main( void )
{
int num, count = 0, ans;
srand(time(0));
ans = rand()00;
printf("Im thinking about a three digit number.\nCan you guess it??\n");
do
{
num = get_int_from_user( "Enter your guess: " );
if(num < ans)
{
printf("Enter a higher number!!!\n");
}
else if(num > ans)
{
printf("Enter a lower number!!!\n");
}
else
{
printf("\n\t =================\n");
printf("\t|| CONGRATULATIONS!! ||\n\t =================\n\nYOU HAVE GUESSED THE CORRECT NUMBER.\n");
}
count ;
} while(num != ans);
printf( "You took %d attempts to answer!\n", count );
return 0;
}
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 output:
Im thinking about a three digit number.
Can you guess it??
Enter your guess: test
Error converting string to number!
Enter your guess: 6abc
Unexpected input encountered!
Enter your guess: 10000000000
Number out of range error!
Enter your guess: 500
Enter a higher number!!!
Enter your guess: 750
Enter a lower number!!!
Enter your guess: 625
Enter a lower number!!!
Enter your guess: 562
Enter a higher number!!!
Enter your guess: 593
Enter a higher number!!!
Enter your guess: 609
Enter a lower number!!!
Enter your guess: 601
Enter a higher number!!!
Enter your guess: 605
Enter a lower number!!!
Enter your guess: 603
Enter a lower number!!!
Enter your guess: 602
=================
|| CONGRATULATIONS!! ||
=================
YOU HAVE GUESSED THE CORRECT NUMBER.
You took 10 attempts to answer!
Further reading about why not to use scanf
: