欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言中scanf函数与输入缓冲区之间的关系

程序员文章站 2022-07-03 11:28:14
...

讨论下scanf函数,输入缓冲区的关系

  样例来源于算法竞赛入门经典第一章实验部分的内容,经过测试发现scanf函数对于整形数据在读入时会过滤掉 空格符 、换行符 和 水平制表符。按照提示,如果b的值非法,比如说输入一个字符‘s’,那么会出现什么结果呢?

void scanf_buffer(void)
{
    int a , b;
    while( scanf("%d%d",&a,&b) != EOF )
        printf("%d %d\n",a,b);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

phase_1:输入的两个数均为合法的int型的整数,可以观察到scanf函数对于整形的读入过滤了回车符、换行符、水平制表符。第一阶段的实验结果明确。

phase_2:当按照提示,b的值在输入时指定为非法,比如说字符‘s’,观察实验结果。在这里观察到了有趣的实验结果,查资料表明这里涉及到了scanf函数的输入格式、返回值和主进程的输入缓冲区等相关内容。

1. scanf函数的读取格式控制&scanf函数的返回值

scanf函数有点意思的,scanf函数会对整形、浮点型和字符数组(字符型指针)过滤掉空格、回车符和水平制表符,对于字符型则按照读入的字符处理。scanf函数按照字符流的形式读取外部输入设备中的字符流,在本例中,过滤结束之后,还需要将字符流“1234”转化为整形1234,字符流“3.14”转化为float型的3.14等。 
C语言中scanf函数与输入缓冲区之间的关系
C语言中scanf函数与输入缓冲区之间的关系
上面两图模拟了一个意欲顺序读入int,float,double型值的一次成功的读取过程。首先scanf函数成功得到了想要的数据,并返回了成功得到的数据的个数。简化逻辑,Keyboard中的数据以中断触发的形式被读入,经过一系列的传递,交付给了系统的输入缓冲区(这时Keyboard中已经没有了数据),然后scanf代表的user通过系统调用成功取走了Input buffer中的数据(Input buffer中数据都被取走,Input buffer为空),然后scanf函数返回值为3,ret = 3。

2. Input buffer

C语言中scanf函数与输入缓冲区之间的关系
如果正常读取的话,也真的不好再说什么了(人家都按照你的意愿达到了你想要的结果,你还想怎样),下面通过一组数据,深入了解一下输入缓冲区。 
C语言中scanf函数与输入缓冲区之间的关系
1、从逻辑上说,输入缓冲区从标准输入或者是文件中得到数据,然后等待被应用进程取走。其必要性是为了匹配低速的输入设备与高速的CPU之间的不均衡性,从而提高系统性能。 
2、为什么非法输入的时候,数据被卡在Input Buffer而不是其他地方? 
注意,过程1只是简单地数据传输,不涉及类型检查,而过程2需要根据scanf函数的格式检查值是否合法,所以问题只可能出在过程2。 
3、这一点比较隐晦,如图的三个变量输入的时候,scanf依然是有返回值的。鉴于scanf函数在处理时候的强次序性,scanf函数返回0代表第一个值已经非法,后面的值合法与否都不在重要了,因为越不过去第一个值;scanf函数返回1代表第一个值合法,第二个值非法,依此类推,直到scanf函数返回3意味着这一轮的数据已经读取完毕,可以进行下一轮的数据读入。

3.解决此题非法输入一直占用Input Bufffer,程序无限循环的方法

方法1:读入的时候顺带检查scanf函数的返回值是否异常,异常也可及时退出,不会一直占用Input Bufffer

void scanf_buffer()
{
    int a , b;
    while( scanf("%d%d",&a,&b) != EOF && scanf("%d%d",&a,&b) == 2 )
        printf("%d %d\n",a,b);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

方法2:fflush(stdin)强制刷新Input Bufffer,保证输入结果的无后效性。及时偶尔的非法输入也不会使程序陷入死循环或直接退出的问题。

void scanf_buffer()
{
    int a , b;
    while( scanf("%d%d",&a,&b) != EOF ){
        fflush(stdin);
        printf("%d %d\n",a,b);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

总结:上述两种方法均可处理scanf异常读入的情况均可以,视不同的情景要求使用。方法2的鲁棒性明显显得好得多,fflush(stdin)也很有意