最近在Linux终端写代码时,使用scanf输入一个字符,当不小心输入了一个退格键或者方向键时,程序就出现无限读取的现象,这显然是scanf的缓冲区内的字符无法刷新出去,即研究一下scanf缓冲区的刷新规则。
scanf缓冲区的刷新规则
当scanf从缓冲区读取走需要的数据之后,缓冲区应该会被清空,但在某些情况下,缓冲区并不会会清空,使用下面的代码检测一下什么情况下缓冲区不会被清空:
输入数字
#include <stdio.h>
int main()
{
int num = 0;
while(1)
{
scanf("%d", &num);
printf("%d\n", num);
}
return 0;
}
[ahao@AHAOAHA test]$ ./test
1 #输入1
1 #输出1
^C
[ahao@AHAOAHA test]$ ./test
w #输入字符w
1
1
1
1
...#输出无数个1
^C
[ahao@AHAOAHA test]$ ./test
^H #输入退格键
0
0
0
0
... #输出无数个0
^C
[ahao@AHAOAHA test]$ ./test
123456787654367654323567654 #输入一个大于INT_MAX的数
-1 #输出-1
^C
通过该例,可以知道当scanf的格式符为"%d"时:
- 输入整型数字:
- 小于INT_MAX:缓冲区正常刷新
- 大于INT_MAX:缓冲区正常刷新,但读取到的数据为-1
- 输入字符:缓冲区不会刷新,则输入的字符会一直保存在缓冲区中,造成scanf不再阻塞等待输入
- 输入退格键等:缓冲区不进行刷新,造成scanf不再阻塞等待输入
输入字符
#include <stdio.h>
int main()
{
char ch;
while(1)
{
scanf("%c", &ch);
printf("%c\n", ch);
}
return 0;
}
[ahao@AHAOAHA test]$ ./test
w #输入字符
w
#换行
#换行
^C
[ahao@AHAOAHA test]$ ./test
#仅输入回车键
#换行
#换行
^C
[ahao@AHAOAHA test]$ ./test
1 #输入数字
1
#换行
#换行
^C
[ahao@AHAOAHA test]$ ./test
^H #输入退格键
#换行
#换行
^C
则当scanf的格式符为"%c"时:
- 输入一个字符:当从键盘输入一个字符时,需要按下回车键表明输入结束,但此时的回车键也会被写入进scanf的缓冲区内,并在下一个scanf是被读出。
- 输入退格键等:依然会被像字符一样对待。
- 输入数字:依然会被像字符一样对待。
经过测试,scanf在读取数据时:
- 当数据的类型与格式符的类型相符时,scanf会读取数据,并将已经读取到的字符从缓冲区中清除
- 当数据的类型与格式符不符时,scanf不会读取该数据,也不会将该数据从缓冲区中清除,这就容易造成scanf不阻塞等待用户输入的现象
清空输入缓冲区的方法
使用while((ChBuf = getchar()) != ‘\n’ && ChBuff != EOF)将缓冲区内的数据全部读出,当读到’\n’或EOF时退出
#include <stdio.h>
int main()
{
char ChBuf = '\0';
char ch;
while(1)
{
scanf("%c", &ch);
printf("%c\n", ch);
//清空输出缓冲区
while((ChBuf = getchar()) != '\n' && ChBuf != EOF);
}
return 0;
}
解决输入字符时连带读取换行符的方法
在读取字符的时候,使用字符串格式读取,将缓冲区的所有内容刷新到一个数组中,再将数组的第一个字符赋给目标字符
- 缺点:
- 如果想要连续读取字符,则字符与字符之间必须使用空格隔开
- 无法读取换行符
#include <stdio.h>
int main()
{
char ch;
char buf[12];
scanf("%s", buf);
ch = buf[0];
return 0;
}