为什么scanf()在这段代码中造成无限循环?

我有一个小型的C程序,它只是从stdin中读取数字,每个循环都有一个数字。 如果用户input一些NaN,应该在控制台上输出一个错误,input提示应该再次返回。 input“0”时,循环结束,给定的正/负值的数量应打印到控制台。 这是该计划:

#include <stdio.h> int main() { int number, p = 0, n = 0; while (1) { printf("-> "); if (scanf("%d", &number) == 0) { printf("Err...\n"); continue; } if (number > 0) p++; else if (number < 0) n++; else break; /* 0 given */ } printf("Read %d positive and %d negative numbers\n", p, n); return 0; } 

我的问题是,在input一些非数字(如“a”)时,会导致无限循环反复写入“ – > Err …”。 我想这是一个scanf()问题,我知道这个函数可以被一个更安全的代替,但这个例子是初学者,只知道printf / scanf,if-else和循环。

我已经阅读了这个问题的答案,并通过其他问题,但没有真正回答这个具体问题。

scanf只消耗匹配格式string的input,返回消耗的字符数。 任何与格式string不匹配的字符都会导致停止扫描,并将无效字符留在缓冲区中。 正如其他人所说,在继续之前,您仍然需要将无效字符从缓冲区中清除。 这是一个非常脏的修复,但它会从输出中删除有问题的字符。

 char c = '0'; if (scanf("%d", &number) == 0) { printf("Err. . .\n"); do { c = getchar(); } while (!isdigit(c)); ungetc(c, stdin); //consume non-numeric chars from buffer } 

编辑:修复代码一次性删除所有非数字字符。 不会为每个非数字字符打印出多个“Errs”。

这是一个很好的scanf概述。

在某些平台(特别是Windows和Linux)上,你可以使用fflush(stdin);

 #include <stdio.h> int main(void) { int number, p = 0, n = 0; while (1) { printf("-> "); if (scanf("%d", &number) == 0) { fflush(stdin); printf("Err...\n"); continue; } fflush(stdin); if (number > 0) p++; else if (number < 0) n++; else break; /* 0 given */ } printf("Read %d positive and %d negative numbers\n", p, n); return 0; } 

scanf()将下一次仍然保留在input缓冲区中的“ a ”。 你可能应该使用getline()来读取一行,然后用strtol()或类似的方法parsing它。

(是的, getline()是GNU特有的,而不是POSIX。那么问题是标签为“gcc”和“linux” getline()也是唯一明智的select,除非你想要读取一行文本全部用手。)

我想你只需要在继续循环之前刷新缓冲区。 像这样的东西可能会做这项工作,虽然我无法testing我从这里写的东西:

 int c; while((c = getchar()) != '\n' && c != EOF); 

而不是使用scanf()并且必须处理具有无效字符的缓冲区,请使用fgets()sscanf()

 /* ... */ printf("0 to quit -> "); fflush(stdout); while (fgets(buf, sizeof buf, stdin)) { if (sscanf(buf, "%d", &number) != 1) { fprintf(stderr, "Err...\n"); } else { work(number); } printf("0 to quit -> "); fflush(stdout); } /* ... */ 

我有类似的问题。 我解决了只使用scanf。

Input "abc123<Enter>"来查看它是如何工作的。

 #include <stdio.h> int n, num_ok; char c; main() { while (1) { printf("Input Number: "); num_ok = scanf("%d", &n); if (num_ok != 1) { scanf("%c", &c); printf("That wasn't a number: %c\n", c); } else { printf("The number is: %d\n", n); } } } 

由于其他答案指出的scanf问题,你应该考虑使用另一种方法。 我总是发现scanf方式对于任何严肃的input读取和处理都是有限的。 用fgets读完整行,然后用strtokstrtol (正确parsing整数并告诉你无效字符开始的位置)这些函数来处理它们是更好的办法。

嗨,我知道这是一个古老的线程,但我刚刚完成了一个学校的任务,我遇到了同样的问题。 我的解决scheme是我使用gets()来获取scanf()留下的内容。

这里是OP代码稍微重写; 可能对他没有用处,但也许会帮助别人。

 #include <stdio.h> int main() { int number, p = 0, n = 0; char unwantedCharacters[40]; //created array to catch unwanted input unwantedCharacters[0] = 0; //initialzed first byte of array to zero while (1) { printf("-> "); scanf("%d", &number); gets(unwantedCharacters); //collect what scanf() wouldn't from the input stream if (unwantedCharacters[0] == 0) //if unwantedCharacters array is empty (the user's input is valid) { if (number > 0) p++; else if (number < 0) n++; else break; /* 0 given */ } else printf("Err...\n"); } printf("Read %d positive and %d negative numbers\n", p, n); return 0; } 

我有同样的问题 ,我发现scanf()被认为是一个破碎的function,因为它造成了很多问题,我find了一个更好的解决scheme。 我使用fgets()来读取input,然后将其input到sscanf() 。 这对于无限循环问题来说并不是一个不好的解决方法,并且对于简单的循环,我告诉Csearch任何非数字字符。 下面的代码将不允许input像123abc

 #include <stdio.h> #include <ctype.h> #include <string.h> int main(int argc, const char * argv[]) { char line[10]; int loop, arrayLength, number, nan; arrayLength = sizeof(line) / sizeof(char); do { nan = 0; printf("Please enter a number:\n"); fgets(line, arrayLength, stdin); for(loop = 0; loop < arrayLength; loop++) { // search for any none numeric charcter inisde the line array if(line[loop] == '\n') { // stop the search if there is a carrage return break; } if((line[0] == '-' || line[0] == '+') && loop == 0) { // Exculude the sign charcters infront of numbers so the program can accept both negative and positive numbers continue; } if(!isdigit(line[loop])) { // if there is a none numeric character then add one to nan and break the loop nan++; break; } } } while(nan || strlen(line) == 1); // check if there is any NaN or the user has just hit enter sscanf(line, "%d", &number); printf("You enterd number %d\n", number); return 0; } 

尝试使用这个:

 if (scanf("%d", &number) == 0) { printf("Err...\n"); break; } 

这对我来说工作得很好…试试这个.. 继续语句不适合作为Err ..应该只执行一次。 所以,尝试打破我testing…这对你工作正常..我testing….

晚上好。 我最近经历了同样的问题,我发现了一个解决scheme,可以帮助很多人。 那么,实际上函数“scanf”在内存中留下了一个缓冲区…这就是无限循环的原因。 所以你实际上必须把这个缓冲区“存储”到另一个variables,如果你的初始scanf包含“null”值。 这是我的意思:

 #include <stdio.h> int n; char c[5]; main() { while (1) { printf("Input Number: "); if (scanf("%d", &n)==0) { //if you type char scanf gets null value scanf("%s", &c); //the abovementioned char stored in 'c' printf("That wasn't a number: %s\n", c); } else printf("The number is: %d\n", n); } } 

当input非数字时,发生错误,非数字仍保留在input缓冲区中。 你应该跳过它。 而且,即使这种符号的组合,例如1a将会首先被读为1,我想你也应该跳过这样的input。

该程序可以看下面的方式。

 #include <stdio.h> #include <ctype.h> int main(void) { int p = 0, n = 0; while (1) { char c; int number; int success; printf("-> "); success = scanf("%d%c", &number, &c); if ( success != EOF ) { success = success == 2 && isspace( ( unsigned char )c ); } if ( ( success == EOF ) || ( success && number == 0 ) ) break; if ( !success ) { scanf("%*[^ \t\n]"); clearerr(stdin); } else if ( number > 0 ) { ++p; } else if ( number < n ) { ++n; } } printf( "\nRead %d positive and %d negative numbers\n", p, n ); return 0; } 

程序输出可能看起来像

 -> 1 -> -1 -> 2 -> -2 -> 0a -> -0a -> a0 -> -a0 -> 3 -> -3 -> 0 Read 3 positive and 3 negative numbers 

扫描前冲洗input缓冲区:

 while(getchar() != EOF) continue; if (scanf("%d", &number) == 0) { ... 

我会build议fflush(stdin) ,但显然这会导致未定义的行为 。

在回应你的评论时,如果你想显示提示,你必须刷新输出缓冲区。 默认情况下,只有在打印换行符时才会发生这种情况。 喜欢:

 while (1) { printf("-> "); fflush(stdout); while(getchar() != EOF) continue; if (scanf("%d", &number) == 0) { ...