C语言中scanf函数与输入缓冲区

    -

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

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

void scanf_buffer(void)
{
    int a , b;
    while( scanf("%d%d",&a,&b) != EOF )
        printf("%d %d\n",a,b);
}

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

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

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

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

2. Input buffer

C语言中scanf函数与输入缓冲区_第3张图片
如果正常读取的话,也真的不好再说什么了(人家都按照你的意愿达到了你想要的结果,你还想怎样),下面通过一组数据,深入了解一下输入缓冲区。
C语言中scanf函数与输入缓冲区_第4张图片
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);
}

方法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);
    }
}

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

你可能感兴趣的:(C)