先观察如下代码和运行情况:
按照控制台提示,输入 value 的值之后,本该能够按照提示继续输入 choice 的值。但是你会发现程序直接跳过了 choice 的输入。而且 choice 原本的值是 'a',最后变成了换行符(即'n')。
造成这种现象的原因是 C 语言的输入缓冲区的机制。
什么是 C 语言的输入缓冲区呢?
输入分为两种,一种称为无缓冲输入(或直接输入),即你每输入一个字符,程序就读一个字符,并可立即使用这个字符;另一种称为缓冲输入,即将你一次性输入的所有字符先收集并存到缓冲区(buffer)里,在你按下 Enter 键之后将这些字符作为一个“块”全部给到程序。
为什么要使用缓冲区呢?
首先,把若干个字符作为一个块来传输比逐个发送字符更节约时间。其次,如果用户打错了字符,可以在输入区域修正,确认无误后按 Enter 键传入正确的输入。如果你使用的是无缓冲输入,那么一旦你打错字,这个错字符立刻就被传入到程序里了,这个时候想要改正就比较麻烦了。无缓冲输入一般在交互性强的程序里使用(比如实时游戏)。
缓冲分为两类:完全缓冲 I/O 和行缓冲 I/O。完全缓冲指的是当输入区被填满时才能刷新缓冲区,通常出现在文件输入中;行缓冲指的是在出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,所以在按下 Enter 键后才刷新缓冲区。
回到之前的问题。当我们输入 value 的值之后,我们敲了一下 Enter,将 value 的值传入,同时刷新了缓冲区。这里注意一个问题:scanf() 里的参数是 %d,要求读取 int 型变量。那么 scanf() 会跳过所有的空白字符,直到遇到第一个非空白字符。如果下一个字符是数字,那么 scanf() 继续读取;如果下一个字符不是数字,则 scanf() 认为读到了末尾,于是刷新缓存区,并将非数字字符放回缓存区。
紧接着我们就要输入 choice 的值,这时在缓冲里的是残留的 Enter,也即换行符。那么 choice 作为一个字符类型的变量,接收到了残留在缓冲区里的 Enter,于是程序自动结束。
因此,如果要让 choice 读到我们输入的正确数据,我们必须要在输入 choice 之前把缓冲区清空。
清空缓冲区的方法有如下三种:
- 使用 stdlib.h 提供的 fflush() 函数。那么原先的代码可以改成如下形式:
fflush() 不在 C/C++ 标准里,比如 Linux 下的 GCC 就不支持这个函数,因此这个方法的可移植性较差,不推荐使用这个方法。
2. 使用 while((choice = getchar()) != 'n' && choice != EOF); 语句。这个方法的原理是循环地将缓冲区剩余的字符赋给 choice,直到将缓冲区里的换行符赋给了 choice 或者 choice = EOF 的时候,则跳出循环。此时缓冲区里没有任何字符。然后即可继续输入。
这个方法的优点是可移植性强,在任何平台上都能过编译。因为只用到了 C 语言的 while 循环。一般推荐使用这个方法。
原先的代码改成如下形式:
3. 使用 stdio.h 提供的 setbuf() 函数关闭缓冲区。