Reputation: 3833
I am learning c and understand that this language is low-level and in this context lacks exception handling.
I made a simple program where the user choose among some alternatives from a menu. Its just that simple!
The program is divided into a few methods - one of the methods waits for the user to press a key - an integer is expected. Then return this integer to another method which holds a switch-structure.
The problem arises when a character is pressed - in most cases an infinite loop of the else-block is started.
You must choose an alternative 0 - 2. Please try again :-)
You must choose an alternative 0 - 2. Please try again :-)
You must choose an alternative 0 - 2. Please try again :-)
...... and so on
I do not actually know how to solve this. I have tried to use the return value from the scanf-function without success. I have also tried to pass a character (instead of integer) as an argument to the scanf-function - also without success.
Any suggestion how to handle this problem?
#include <stdio.h>
#include <stdlib.h>
void menu();
void runSelection(int selection);
int getSelection();
int pause();
int main(void) {
do{
menu();
runSelection(getSelection());
}while(pause());
return 0;
}
int pause() {
int c;
printf("\n\nPress enter to continue!");
fflush(stdout);
/* flush inputstream */
while((c = getchar()) != '\n' && c != EOF);
getchar();
return 1;
}
void menu() {
puts(" * * * * * * M E N U * * * * * * *");
puts("1. Do something 1");
puts("2. Do something 2");
puts("3. Do something 3");
fflush(stdout);
}
void runSelection(int selection) {
switch (selection) {
case 0:
puts("you pressed 0");
break;
case 1:
puts("you pressed 1");
break;
case 2:
puts("you pressed 2");
break;
}
}
int getSelection() {
int key;
int true = 0;
do {
scanf("%d", &key);
if (key >= 0 && key <=2) {
true = 1;
}
else {
puts("You must choose an alternative 0 - 2. Please try again :-)");
fflush(stdout);
}
} while (true == 0);
return key;
}
Upvotes: 1
Views: 3098
Reputation: 844
Edit: Back when I first wrote this answer, I was so stupid and ignorant about how scanf()
worked.
scanf()
is not a broken function, if I don't know how scanf()
works and I don't know how to use it, then I probably haven't read the manual for scans()
and that cannot be scanf()
's fault.scanf()
works.When you use scanf("%d", &key)
in your code, the scanf(
) tries to read in an integer from the input, but if you enter a non numeric value, scanf()
knows it isn't the right data type, so it puts the read input back into the buffer, on the next loop cycle however the invalid input is still in the buffer which will cause scanf()
to fail again because the buffer hasn't been emptied, and this cycle goes on forever.
In order to tackle this problem you can use the return value of scanf()
, which will be the number of successful inputs read, however you need to discard the invalid inputs by flushing the buffer in order to avoid an infinite loop, the input buffer is flushed when the enter
key is pressed, you can do this using the getchar()
function to make a pause to get an input, which will require you to press the enter key thus discarding the invalid input, note that, this will not make you press the enter
key twice whether or not you entered the correct data type, because the newline character
will still be in the buffer. After scanf()
has successfully finished reading the integer from input, it will put \n
back into the buffer, so getchar() will read it, but since you don't need it, it's safe to discard it:
do
{
status = scanf("%d", &key);
getchar(); // flush the buffer by reading a character and discarding it
if (key >= 0 && key <=2)
{
true = 1;
}
else
{
puts("You must choose an alternative 0 - 2. Please try again :-)");
fflush(stdout);
}
} while (true == 0 && status != 1);
Upvotes: 0
Reputation: 43518
you got an infinite loop because the scanf is reading a charachter form the input buffer. there is a remaining characheter in the buffer which the newline '\n'
Use the following macro instead of using scanf directelly
#define SCAN_ONEENTRY_WITHCHECK(FORM,X,COND) \
do {\
char tmp;\
while(((scanf(" "FORM"%c",X,&tmp)!=2 || !isspace(tmp)) && !scanf("%*[^\n]"))\
|| !(COND)) {\
printf("Invalid input, please enter again: ");\
}\
} while(0)
int main()
{
int decision;
printf("Input data, valid choice 1 or 0: ");
SCAN_ONEENTRY_WITHCHECK("%d",&decision,(decision==0 || decision==1));
printf("You have entered good input : %d\n", decision);
}
The following topic contains an explaination about this macro and how to use it: Common macro to read input data and check its validity
Upvotes: 2
Reputation: 47784
Why you didn't remove trailing '\n'
in getSelection
like everywhere else ?
Use:
int c;
scanf("%d", &key);
while((c = getchar()) != '\n' && c != EOF); //Add this line
in getSelection
too
Upvotes: 0