问题
I'm using a function called checkType to check whether the user has entered a valid input of integer type. For example, if the user enters 15 it will print valid and 15c will print not valid. However, if the user enters a string input only like ccccc, it results in an infinite loop and the program crashes. I have added some screenshots below to show the outputs.
int checkType(int input, char *c) {
if (input == 2 and *c == '\n') {
return 1;
} else {
return 0;
};
}
int main(void) {
int faces = 0;
char c;
int valid = 0;
int input;
while (valid == 0) {
printf("Enter number of faces: ");
input = scanf("%d%c", &faces, &c);
valid = checkType(input, &c);
printf(valid == 0 ? "not valid\n" : "valid\n");
}
}
Infinite Loop:
回答1:
The scanf()
family of functions is not really made for input of questionable syntax.
The usual approach to solve your problem is to read in all the input in a way which is sure to succeed, e.g. by reading a complete line (instead of a number, word-like string, or anything else with expected format) with fgets()
.
Then you can try to parse that line-representing string as by expected format. (You can use sscanf()
for the parsing attempt.) If that fails you ignore it and read the next line, or try parsing according to an alternative allowed format for the same input.
The relevant difference is that reading a whole line will succeed and remove it from the input stream. In contrast to that, your code, in case of syntax failure, leaves in the input stream whatever did not match the expected syntax. As such it will, of course, fail the next iteration of the reading loop again. That is what causes your endless loop.
In detail:
- read a whole line into a buffer,
usingfgets()
and the option to restrict the number of characters to the size of the buffer - at this point all of the line is removed from the input (aka stdin, aka input stream),
which means that the next read will get new input even if the read line does not match any allowed format,
this is the relevant difference to trying to read input directly withscanf()
- from the line buffer, attempt to read separate values according to an allowed format,
usingsscanf()
and with a check of the return value - if successful great, you have your expected variables filled (e.g. an integer);
you could try scanning for additional, not format-covered trailing input (and continue like for incorrect input, even if the beginning of the line matched an allowed format) - if not successful try a different allowed format,
usingsscanf()
again,
from the same line buffer, which is not changed, not even if a format partially matched - if no allowed format matches the input it is time to consider it incorrect
- incorrect input can be ignored or can cause a fatal parsing error in your program, your choice
回答2:
However, if the user enters a string input only like ccccc,
it results in an infinite loop and the program crashes
Reason : scanf
returns the number of valid read values from stdin
. If we give cccc
as input, scanf
cannot read the value, because the input and variable faces
are of different data type. This makes input = 0
. So the function checkType
returns 0, which in turn makes valid = 0
. This makes while(valid == 0)
always true and hence the endless loop.
回答3:
Scanf will read form stdin, and not clean the stdin buffer if not match. So it will read wrong data again in your case, you can just add a clean function with stdin after scanf, like: __fpurge(stdin)
You can refer to the following two links:
https://man7.org/linux/man-pages/man3/scanf.3.html
http://c-faq.com/stdio/stdinflush2.html
回答4:
With input like "ccccc"
and scanf("%d%c" ...
, there is no conversion to an int
. scanf()
returns 0 and stdin
remains unchanged. Calling the function again has the same result.
Code neds to consume errant input - somehow.
Research fgets()
to read a line of user input.
来源:https://stackoverflow.com/questions/64870792/scanf-not-working-in-while-loop-when-nonmatching-string-is-entered