[讨论]FAQ新增条目建议 输入缓冲区垃圾及处理
情况1首先让我们来看看下面的程序
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
scanf("%c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}
输入两个整数,并赋给a与b,然后输入两个字符给c1与c2,输出c1与c2。这个程序看上去是没有错误的。实际操作一下看看。#include<stdlib.h>
int main(void)
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
scanf("%c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}
输入:
12 34
a b
输出:
c1='
',c2=' '
这很明显和我们所期望结果的不同,它的错误是在输入12,34并回车后,程序将输入缓冲区中的\n直接读入了,结果造成c1='\n'。下面提供两种解决方法。
第一种:死了都要C提供的方法
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
getchar();
scanf("%c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}
他在第二个scanf前加了一行getchar();经测试可以显示c1=a,c2=b了。那么这个方法的原理是什么呢,用错误数据来测试一下。#include<stdlib.h>
int main()
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
getchar();
scanf("%c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}
输入:
12 34a
b c
输出:
c1='
',c2='b'
换一个输入试试
输入:
12,34abcd(输入两个或两个以上的字符)
(输入c1和c2的过程直接被程序跳过了)
输出:
c1='b',c2='c'
从以上的输入和输出可以看出,getchar()似乎并没有将输入缓冲区清空,只是向后跳了一个字符,即c1等于"12,34"后第二个被输入的字符。所以正常输入时,"12,34"后的"\n"被跳过了,使得c1=a,c2=b。当然,平常输入的时候也不会有人白痴到在"12,34"后再输入其他的字符,我这样做的原因只是想要说明在我的理解中用getchar()是怎样达成正确输入的效果的。
再来看看第二种方法:百年不亮提供的方法
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
while(getchar()!='\n');
scanf("%c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}
这个方法在第二个scanf前加了一行while(getchar()!='\n');经测试没有问题,那么同样输入错误数据看看。#include<stdlib.h>
int main(void)
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
while(getchar()!='\n');
scanf("%c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}
输入:
12 34a
b c
输出:
c1='b',c2='c'
输入:
12 34abcdef
b f
输出:
c1='b',c2='f'
可以看出,在用while(getchar()!='\n');的情况下"12,34"后的第一个"\n"前的所有字符都被清空了。这两种方法都可以使用,不过比较下来应该是第二种方法比较实用。当然,谁有更好的方法也请提出来,知识是不嫌多的。
情况2
再来看另一个程序
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
int main(void)
{
int result,n1,n2,n,flag;
char c;
do{
flag=1;
printf("\nChoose an operator:1.+ 2.- 3.* 4./:\n");
scanf("%d",&n);
printf("Enter two numbers:\n");
scanf("%d %d",&n1,&n2);
switch(n)
{
case 1: result=n1+n2; break;
case 2: result=n1-n2; break;
case 3: result=n1*n2; break;
case 4: result=n1/n2; break;
default: printf("Bad operator!\n");
flag=0;
}
if(flag)
printf("The result is %d\n",result);
printf("Would you like to do another problem?(Y/N):\n");
c=getch();
}while(c=='y'||c=='Y');
return 0;
}
这是一个整数计算器程序,首先选择要用那种算法,然后输入两个数字,将结果输出后询问是否要继续运行程序,输入"y"或"Y"后程序从头运行,输入任意键退出。如果在选择算法的时候输入了错误的数字,则输出"Bad operator!"并询问是否要继续运行程序。这个程序本身没有什么问题,不过在一些错误的输入下就会有问题了,运行看看。#include<stdlib.h>
#include<conio.h>
int main(void)
{
int result,n1,n2,n,flag;
char c;
do{
flag=1;
printf("\nChoose an operator:1.+ 2.- 3.* 4./:\n");
scanf("%d",&n);
printf("Enter two numbers:\n");
scanf("%d %d",&n1,&n2);
switch(n)
{
case 1: result=n1+n2; break;
case 2: result=n1-n2; break;
case 3: result=n1*n2; break;
case 4: result=n1/n2; break;
default: printf("Bad operator!\n");
flag=0;
}
if(flag)
printf("The result is %d\n",result);
printf("Would you like to do another problem?(Y/N):\n");
c=getch();
}while(c=='y'||c=='Y');
return 0;
}
Choose an operator:1.+ 2.- 3.* 4./:
(输入1.1)
Enter two numbers:
The result is 2293730
Would you like to do another problem?(Y/N):
(输入y)
Choose an operator:1.+ 2.- 3.* 4./:
Enter two numbers:
The result is 2293730
Would you like to do another problem?(Y/N):
可以看到,输入两数的过程被程序跳过了,然后当继续运行的时候,程序就开始反复输出之前的内容。造成这种情况的原因是scanf("%d",&n);只能接受整数,当输入非整数时,输入的数据就会遗留在输入缓冲区中。而由于缓冲区中有数据,scanf函数就不会等用户输入,直接从缓冲区中读取数据,结果读取到的还是无法接受的数据,这个数据再次被留在缓冲区中,就这样反复不止。那么这种情况应该如何解决呢?看看josen0205提供的方法
#include<stdio.h>
#include<conio.h>
int main()
{
int result,n1,n2,n;
char c;
while(1)
{
printf("\nChoose an operator:1.+ 2.- 3.* 4./:");
if(scanf("%d",&n)!=1||n<1||n>4)
{
printf("Bad operator!\n");
fflush(stdin);
continue;
}
printf("Enter two numbers:");
scanf("%d %d",&n1,&n2);
switch(n)
{
case 1: result=n1+n2; break;
case 2: result=n1-n2; break;
case 3: result=n1*n2; break;
case 4: result=n1/n2; break;
default :
break;
}
printf("The result is %d\n",result);
printf("Would you like to do another problem?(Y/N):");
c=getch();
putchar(c);
if(c=='n'||c=='N')
break;
}
getch();
return 0;
}
#include<conio.h>
int main()
{
int result,n1,n2,n;
char c;
while(1)
{
printf("\nChoose an operator:1.+ 2.- 3.* 4./:");
if(scanf("%d",&n)!=1||n<1||n>4)
{
printf("Bad operator!\n");
fflush(stdin);
continue;
}
printf("Enter two numbers:");
scanf("%d %d",&n1,&n2);
switch(n)
{
case 1: result=n1+n2; break;
case 2: result=n1-n2; break;
case 3: result=n1*n2; break;
case 4: result=n1/n2; break;
default :
break;
}
printf("The result is %d\n",result);
printf("Would you like to do another problem?(Y/N):");
c=getch();
putchar(c);
if(c=='n'||c=='N')
break;
}
getch();
return 0;
}
他的程序中用if(scanf("%d",&n)!=1||n<1||n>4)来判断输入的数据是否在(1,4)的区间中,如果输入数据不在该区间中则输出"Bad operator!",用fflush(stdin);清空缓冲区并重新要求输入数据。用他的方法可以很好的解决类似问题。
最后做一下总结:
1、在连续有多个scanf语句时,最好将%c格式放在最前面,或使用getchar();或while(getchar()!='\n');类的语句跳过"\n"
2、在上述的第二个程序的那种情况下,最好使用fflush(stdin);来清空输入缓冲区。
以上就是我所遇到的两种情况,如果还有其他的情况,请大家补充。
ok.我把输入的部分都改成了空格分隔。
另外,在帖子“缓冲流问题,输入一个数后,下一个不能再读取(printf,scanf的问题)”中http://bbs.bc-cn.net/viewthread.php?tid=171684又学到了一种解决方法:
在连续有多个scanf语句时,如果%c在后面的话,可以遵照以下方法
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
scanf(" %c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}
#include<stdlib.h>
int main(void)
{
int a,b;
char c1,c2;
scanf("%d %d",&a,&b);
scanf(" %c %c",&c1,&c2);
printf("c1='%c',c2='%c'\n",c1,c2);
system("pause");
return 0;
}
在%c前加一个空格,它的作用是,“scanf的控制字符串中加空格可以过滤掉空白字符(如回车,制表)
scanf(" %c",&a); %c前的空格可以过滤掉上次敲回车后留下的'\n',从第一个非空白字符开始读。
建议看《c primer plus》第八章 字符输入/输出和输入确认”(引用自百年不亮的解释)
特别感谢死了都要C,百年不亮,josen0205和felicia!
[此贴子已经被作者于2007-9-22 17:45:52编辑过]