输入一个字符作为整数变量的输入,给出一个无限循环
#include #include int main() { int number, sum = 0; // loop body is executed at least once do { printf("Enter a number: "); scanf("%d", &number); sum += number; } while(number != 0); printf("Sum = %d",sum); }
该代码应该打印用户输入的所有整数的总和。 但是当用户输入一个字母作为输入时,它不会抛出错误,而是进入无限循环,打印“输入数字”语句但不在循环中运行任何其他语句。
有人能告诉我为什么会这样吗?
对代码的正确修复将是以下代码段。 检查scanf()是否成功,然后继续
if (scanf("%d", &number) != 1) { break; }
scanf
很奇怪。 它有一个内部缓冲区,只有当缓冲区为空时才会从流中读取。 在代码的开头,什么都没有,所以从标准输入读取一行。 然后scanf
尝试在那里找到一个整数。 如果找到一个整数,它将报告它成功读取一个项目(通过返回1
)并将读取值放入提供的指针位置。 但是,如果未找到整数,则“零项成功读取”将返回0
,并且不会从缓冲区中消耗任何内容 。 这意味着,下次调用scanf
,缓冲区不会为空,因此不会再次提示用户输入新行, scanf
将再次尝试读取与上次尝试完全相同的整数(并且失败的原因完全相同)。 因此,无限循环。
为了说明这个“缓冲”的事情,试试这个:
scanf("%d", &a); scanf("%d", &b); printf("a: %d, b: %d\n", a, b);
并在一行中输入12 53
,作为第一个scanf
的输入。 可悲的是, a
最终为12
, b
最终为53
。 神奇的解释是 – 第一个scanf
发现缓冲区为空,从用户读取行,并找到一个整数,因为它被告知; 第二个scanf
发现缓冲区中仍然存在一些东西,并继续读取它最后离开的位置,而不必询问用户是否有新行。
正如其他人已经评论过的那样,通常最好自己阅读一行( fgets
)并自己解析( strtol
),或者如果你绝对需要使用scanf
,请务必检查scanf
是否通过检查它的返回值来读取你预期的内容码。 如果scanf
失败,请参见此处有关如何清除输入缓冲区的信息。
scanf()
不会丢弃与格式说明符不匹配的输入。 您应该在带有%s
的char []中读取它,然后执行atoi()
以尝试将其转换为字符串。 如果失败, ERRNO
将被设置为一些错误代码。 这样,如果输入的数字不是数字,您将不会留下任何未处理的输入。
这个问题已在这里得到解答。
引用答案:
%d转换说明符期望输入文本格式化为十进制整数。 如果不是,则转换失败,导致转换失败的字符将保留在输入流中。 使用%d转换说明符进一步调用scanf()将阻塞相同的字符。
这基本上意味着当您使用%d
作为转换说明符调用scanf()
时,它会期望用户输入十进制整数。 但是,当用户输入字符(%c)作为输入时,转换失败并且输入的字符保留在输入流中。 这将导致while循环永远继续,而不允许用户输入任何更多输入。
为了防止这种情况,您可以尝试以下方法:
while (x != 0){ int tmp; if (scanf("%d", &tmp) == 0) getchar(); else number = tmp; }
其中一种可能的解决方案是在scanf之后添加输入缓冲区清理代码,例如使用getchar循环:
#include #include int main() { int number = -1; // any number not equal 0, to prevent loop break // after wrong input at the first iteration int sum = 0; int c; // char to read input // loop body is executed at least once do { printf("Enter a number: "); if(scanf("%d", &number) == 1) { sum += number; // add if read was successful } // buffer clean while ((c = getchar()) != '\n' && c != EOF); } while(number != 0); printf("Sum = %d",sum); }
输入缓冲区清理的已知选项:
-
fseek(stdin,0,SEEK_END);
-
fflush(stdin);
(对于某些编译器和操作系统,它可以工作)