虽然这是一个非常简单的问题,但我正在编写一个程序,它被卡在无限的“while”循环中,因为打破循环的参数由需要用户输入的switch语句决定。问题是程序永远不会停止输入,所以它不断选择默认情况,导致无限循环。
到目前为止,这是我的代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
float nickels, dimes, quarters, total, price;
char input;
price = 1.00;
total = 0;
while(total < price){
printf("n = nickel, d = dime, q = quarter: ");
scanf("%c", &input);
switch(input){
case 'n': case 'N':
total += 0.05;
printf("\nTotal: %f\n", total);
break;
case 'd': case 'D':
total += 0.10;
printf("\nTotal: %f\n", total);
break;
case 'q': case 'Q':
total += 0.25;
printf("\nTotal: %f\n", total);
break;
default:
printf("\nWrong input\n");
break;
}
}
printf("You're done! Total is %f", total);
return 0;
}
我知道这是一个非常简单的问题。我为此道歉。它很可能是我只是忽略的东西。
答案 0 :(得分:2)
简而言之,scanf()
正如您所使用的那样消耗了用户的输入字符,但是没有使用他必须输入的换行符,而是将其留给循环的下一次迭代来跳过。最简单的解决方法是通过添加
case '\n': break;
到switch
语句,以便它们被静默消耗而不是触发default
个案。
那就是说,这里有一个更详细的解释:幕后发生了什么:
一般来说,I / O操作比初学者书籍和课程更复杂。 C运行时库促进的I / O的自然形式具有以下优点:它隐藏了灵活的“流”概念背后的数据访问的大部分细节。
流通常是缓冲的,这意味着对流的读取和写入实际上作用于内存缓冲区,以便实际上将数据移入和移出内存的昂贵系统调用可以更少发生,并且一次更多字节。处理大型文件时,这是一个巨大的好处,并且可以一次处理整个文件显然是一个字符。
完全缓冲的流对于交互式使用来说确实很不方便,因此典型的实现方式会使stdin
,stdout
和stderr
流成为“行缓冲”与交互式终端相关联。当输出流是行缓冲时,它会在打印换行符时将缓冲区刷新到终端。当输入流是行缓冲时,它一次填充一行输入缓冲区。
这尤其意味着第一次调用scanf()
(或getchar()
或stdin
上的任何其他读取操作)显然会阻止执行,直到用户输入换行符,然后继续返回预期返回的内容。
在您的情况下,scanf("%c", &input);
会阻止,直到用户输入换行符,然后将键入的第一个字符复制到input
,并在其中留下任何其他字符和换行符缓冲。下一次循环,scanf("%c", &input);
将从缓冲区中取出下一个字符,而不等待任何进一步的输入。我们知道至少有一个这样的字符,因为第一个循环等待输入“d \ n”然后只消耗“d”,留下“\ n”以供日后使用。
今天普遍接受的最佳做法是根本不使用scanf()
。使用fgets()
然后适当地解析整行输入会好得多。对于您的示例,您可以读取一行,然后计算所有'n','d'和'q'次出现。当然,细节取决于您应该实施的规范。
我提到了“buffered”和“line buffered”流。第三种流更适合交互式使用。通常可以使流本身为“无缓冲”,这样每次调用stdio函数都会立即在设备或文件中生效。仅建议交互式程序使用无缓冲操作,因此要使用它,您应验证stdin
是否真正附加到终端而不是从文件重定向。您还需要处理这样一个事实,即终端设备驱动程序本身几乎肯定会尝试以最小的行缓冲其输入。将有一种特定于系统的方法将终端驱动程序置于原始模式,其中每个字符类型将立即从read(2)
系统调用返回,允许程序响应各个按键而无需等待回车键被迫。实际上,配置此操作模式非常棘手,特定于平台,并且超出了本问题的范围。