• 解决scanf或者cin造成的死循环问题


      最近有个同学拿了一个程序说让我帮忙给调试一下,我拿到看了一下发现,问题确实很怪异,他在写一个console程序并且希望能有一个简单的菜单,用户输入0-5之间的整数进行选择,输入错误会输出提示信息并要求重新输入,但是如果用户输入是一个字符的话,程序就会陷入死循环,不停地打印提示信息。

      问题明确了,答案也就出来了,显然是因为scanf函数或者cin读取输入字符时的问题,我用C写了小程序来演示这个问题,代码如下:

      

    #include <stdio.h>
    
    int main()
    {
    	int	number;
    
    	printf("请输入0-5之间的数字:\n");
    	for( ; ; )
    	{
    		scanf("%d",&number);
    		if(number >= 0 && number <= 5)
    		{
    			printf("你输入的数字是%d。\n",number);
    			break;
    		}
    		else
    		{
    			printf("输入有误,请重新输入!\n");
    		}
    	}
    	return 0;
    }
    

      造成死循环的原因也很简单,当第一次通过scanf函数读入整数时,如果我们输入的是一个字符,那么scanf将会什么都不读,直接返回0,输入错误提示信息后再次调用scanf读取时,由于我们上次输入的字符依旧在输入缓冲区里,所以scanf不需要等用户输入就直接返回0,然后第三次……第四次……这就是死循环的原因了。

      那怎么解决呢?也很简单,只要在for循环内部调用scanf函数前添加一句fflush(stdin)来强制刷新输入缓冲区就可以了,这样输入缓冲区里就又是空的了,所以scanf会等待用户输入。

      感谢键盘农夫在评论中指出了一个影响可移植性的问题:fflush(stdin)是未定义行为。查看了一下ISO9899.1999发现确实是这样的,从MSDN的介绍来看VC是完全支持这种做法的,其它编译器就不好说了,于是我找了另一个清空输入缓冲区的方法Flush the input buffer,但是这个方法在这里使用的话或许会影响代码可读性,可以自己创建一个FlushStdinBuffer函数完成这件工作。本段是May/06/2011新加内容。

      还可以怎么解决呢?我的同学提出了一个方法,就是将number不定义成int型,而是char型,那么到底行不行呢?乍一看上去似乎是可以的,但是如果用户输入是1287dst呢?程序将会只读第一个字符'1',然后认为这是合法的输入,这显然也是有问题的。

      那么不使用scanf函数,然后用getchar函数呢?还是有些问题,因为如果用户输入是一个字符的话,getchar将会读取两次,第一次读到的是对应字符的ASCII码,第二次读到的是那个换行符0x0A,所以错误提示信息也会出现两次。getc函数也会引起同样的结果,所以如果一定想用这两个函数的话,就要自己在for循环内部过滤掉对0x0A的判断。那么getch呢?这个函数显然是可以的,因为这个函数虽然也跟前面两个函数一样读输入,但是它不需要用户按回车就可以读取,所以只会读到用户输入的那一个字符,只是不回显罢了。

      显然,上面我们只提到了读取一个字符的函数,并没有提到gets之类的读取字符串的函数,因为这个小程序只允许输入字符,不过如果是其它程序需要这样做的话,gets是完全可以完成这个要求的,而且不会出现上面这么多问题,但是可一定不要忘记了缓冲区溢出哦。

      而C++用std::cin读取整型数字的时候也会出现scanf函数的问题,当输入不是整数时,它也是直接返回,下次就死循环了,所以在调用cin前可以先调用cin.clear()和cin.sync(),单独调用一个是没有用的哦,你或许会疑惑这是为什么呢?

      其实cin.clear()的作用并不像它的名字一样,当程序想要去读一个int型却读到了一个char型输入的时候,cin就会将自己的内部错误标识符设定为ios::failbit(没有错误是ios::goodbit),cin.clear()的作用不是清空输入缓冲区,而是清空这个内部错误标识符,真正清空输入缓冲区的是cin.sync(),但是只清空缓冲区也不行,因为内部错误标识符还保留着呢,下次读取的时候一看上次有错误,这次根本不读了……所以一定要一起调用。

      好了,关于这个问题就谈到这里了,如果你也有同样的问题依旧没有解决的话欢迎留言,我们一起讨论学习。

  • 相关阅读:
    1206 冲刺三
    1130持续更新
    1128项目跟进
    冲刺一1123(总结)
    冲刺一
    1117 新冲刺
    0621 第三次冲刺及课程设计
    0621回顾和总结
    实验四主存空间的分配和回收
    学习进度条
  • 原文地址:https://www.cnblogs.com/pianoid/p/2035656.html
Copyright © 2020-2023  润新知