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

来源:互联网 发布:ubuntu jdk 1.7 编辑:程序博客网 时间:2024/04/28 06:35

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

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

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#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(),但是只清空缓冲区也不行,因为内部错误标识符还保留着呢,下次读取的时候一看上次有错误,这次根本不读了……所以一定要一起调用。

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


0 0