声明:此文章仅作为C语言学习历程中的思路整理,用于解决入门阶段较为浅显的问题,如有错误还请指正。
IDE:小熊猫 Dev-C++
现象描述:
代码要求实现用户输入1或2,通过scanf("%d",&a)读取这个值,从而判断分属于1或2的两种情况。输入非1,2的数字都应该返回"Invalid Number!",并且要求重新输入数字。
while(a!=1&&a!=2)
{
printf("Invalid Number!\n");
printf("Enter the number:");
scanf("%d",&a);
}
该程序在用户输入整数时均可以正常运行,但是一旦用户输入"a","aasa" 时,程序会陷入死循环。死循环和while语句本身没有关系,无论使用while,goto,for,只要是循环体,均会产生此问题。
程序在后续执行中直接跳过应该由用户输入的scanf("%d",&a);
原理分析:
经多方查找资料得,scanf有缓存区,当输入非法字符(要求的类型与输入的类型不符合),scanf会直接跳过,该输入及不会被接受也不会被清除,被存放在scanf的缓存区,当下次调用scanf函数时,会直接从缓存区读取非法字符,造成死循环。
解决思路:
清除滞留在scanf缓存区的数据,使下一次循环时scanf可以正常的读取用户输入。
解决方法:
1.fflush(stdin);
直接使用缓冲区清除函数fflush(stdin); //stdin代表标准输入;
但fflush(stdin)-这个只能保证在VC上可以生效,在其他编译器上不保证可以起作用。
如果在Linux系统中使用gcc编译,该方法不可用。
资料:
在windows VC下fflush(stdin)是可以实现的,但是linux下不可以。 C标准规定fflush()函数是用来刷新输出(stdout)缓存的。对于输入(stdin),它是没有定义的。但是有些编译器也定义了 fflush(stdin )的实现,比如微软的VC。其它编译器是否也定义了 fflush( stdin)的实现应当查找它的手册。GCC编译器没有定义它的实现,所以不能使用 fflush( stdin )来刷新输入缓存。
2.getchar();
浏览过一些文章,单独使用一行getchar();可以避免单个字符造成的错误,但当输入多个字符时,多出的字符仍会造成多余的循环。
3.通过循环使getchar()读取多次非法字符,直到清除。
while(a!=1&&a!=2)
{
printf("Invalid Number!\n");
printf("Enter the number:");
while (getchar() != '\n');
scanf("%d",&a);
}
可以解决多个非法字符输出的问题。
4.实现fflush(stdin);
既然fflush(stdin)为未定义行为,我们可以自己编写代码简单实现此函数。
char b;
do
{
scanf("%c",&b);
}
while(b !='\n');
这里,使用scanf读取了多余的数据。
char c;
while((c=getchar())!='\n'&& c!=EOF);
这里使用getchar()读取多余数据。
往往一个程序中需要多次使用scanf函数,我们将其单独封装,方便多次调用。
此方法也适用于gcc编译器。
void cl(void)
{
char b;
do
{
scanf("%c",&b);
}
while(b !='\n');
}
if(a!=1&&a!=2)
{
printf("Invalid Number!\n");
printf("Enter the Unit of Weight ([1]-lbs, [2]-kg):");
cl();
scanf("%d",&a);
}
注意:几种方法应该代码位置应该写置scanf之前,若放置之后,虽然能清除缓存区,但仍会多循环一次,推测是由回车造成的,没有进行验证。