转载:https://blog.csdn.net/Veniversum/article/details/62048870

对C 语言初学者来说,fflush(stdin)函数被解释为会清空输入缓冲区的一个系统函数,这是一个曾经几乎对过一半的说法,随着计算机科学的进步,在学习的过程中的逐步完善,将fflush(stdin)函数的过去与现在分析一下。


Personal thinking:

fflush(stdin) 会清空输入缓冲区中的内容,读取时输入缓冲区中的内容会被scanf函数逐个取走,正常case下scanf()函数可以根据返回值判断成功取走的数目;当发生异常读取的时候,如应该读取一个整形,结果输入缓冲区内当前的内容是个字符串,发生读取异常。发生读取异常之后,输入缓冲区中的内容并未被取走,那么下次循环之时,scanf()函数发现输入缓冲区中有内容(显然编译器不会关心这个内容是不是合法),于是不再等待user输入,直接尝试读取输入缓冲区中的内容,显而易见的又是一次读取异常,如此反复。

include <stdio.h>
int main( void )
{
int val,ret;
while(fflush(stdin),(ret = scanf("%d",&val)) != EOF)
printf(“%d\n”, val);
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

按照直觉,这个程序使用了fflush(stdin)以确保读取异常时不干涉下一次的读取。那么问题来了,事实果真如此么?

fflush(stdin)的前世今生
由Microsoft官方提供的MSDN 文档里也清楚地写着:fflush on input stream is an extension to the C standard(fflush 操作输入流是对 C 标准的扩充,注意啊,是Extension!)。即C 标准中根本没有定义 fflush(stdin),最新的C 11直接删去了曾经打擦边球的fflush(stdin)。
这也就是开头说的fflush(stdin)曾经几乎对过一半的原因:fflush()确实也是扩展,这就是几乎对过的意思,在vs 2013之前的版本里,包括 vc++ 6.0 fflush(stdin)也确实管用。现在即使在vs 2015环境下fflush(stdin)也不再起作用,遑论lunix系统下呢,这就是曾经对一半。
当然,如果毫不在乎程序的移植性,在可用 fflush(stdin)的版本里面这么写也没什么大问题。


清空输入缓冲区的可替代方法

既然fflush(stdin)现在在vs 2015 下不干活了,那么总得有接替背锅的角色,实现清空缓冲区的角色,下面根据查阅的结果,给出两种在C 可以实现清空输入缓冲区功能的可行方案。
首先声明下,使用setbuf(stdin,NULL)是GCC下可用的一种方法,但是没有解决掉缓存的问题,然而这里不予深究。
在vs 2015 下,可以用下面两种方法代替fflush(stdin)实现功能:
1)使用函数rewind(stdin)
从函数名上来看,这个函数应该是重定义了输入缓冲区的Location or Size,用这个方法得到新缓冲区,前后两个输入缓冲区并不是一样的(纯猜测,轻点打脸!),其实这个函数好像也是非标准定义的(不是很确定,因为在 C 11与 C 99 的 更新里面真没看到这个,不过也可以作为暂时管用的半个。都说到这里,多说一句,C 11 标准确实地删除了gets()函数,用gets_s()代替,关于这一点放在最后一个链接内)

2)使用scanf(“%*[^\n]%*c”),原理是用扫描集将缓冲区中的字符全部读取来实现清除输入缓冲区的动作,就效果来说非常管用,而且还跨平台。
乍一看这东西还有点深奥,给出详细的解释,也可以参考链接里面的解释说明。
对scanf(“%*[^\n]%*c”)解释:
〔^\n〕将逐个读取缓冲区中的’\n’字符之前的其它字符,%后面的表示将读取的这些字符丢弃,前遇到’\n’字符时便停止读取操作,此时,缓冲区中尚有一个’\n’字符遗留,所以后面的%*c将读取并丢弃这个遗留的换行符,这里的星号和前面的星号作用相同。由于所有从键盘的输入都是以回车结束的,而回车会产生一个’\n’字符,所以将’\n’连同它之前的字符全部读取并丢弃之后,也就相当于清除了输入缓冲区。


关于fflush(stdout)

如果不考虑fflush(stdin)这个坑的话,它的兄弟fflush(stdout)还是有很大的作用的,简言之,fflush(stdout)强制输出当前输出缓冲区中的内容,一些在Debug下一些莫名其妙的error可以用fflush(stdout)立即输出在处理过程中的中间结果来确定error所在。
在查阅过程中发现一句话:
fflush(stdin)对输入流的操作是未定义的,所以这个还是要慎用,或许有副作用。
fflush(stdout)只是将需要输出的输出缓冲区中内容当即输出,利于调试且没有什么不良后果。

不管怎么说,向当年的二极管一样,fflush(stdin)也在计算机的发展过程中广泛使用过,时过境迁。一回首已百年身,也许以后的学习过程中再也不会遇见fflush(stdin)了。


参考资料:

1.ISO/IEC 9899:1999 (E) Programming languages— C 7.19.5.2 The fflush function
2.The C Programming Language 2nd Edition By Kernighan & Ritchie


相关链接:

scanf(“%[^\n]%*c”)怎么理解
scanf与缓存
C语言格式输入函数scanf()详解
用rewind(stdin)代替fflush(stdin)
C语言标准中的C99与最新的C11

C 清空输入缓冲区,以及fflush(stdin)的使用误区和解决方法的更多相关文章

  1. C语言清空输入缓冲区的N种方法对比

    转自C语言清空输入缓冲区的N种方法对比 C语言中有几个基本输入函数: //获取字符系列 int fgetc(FILE *stream); int getc(FILE *stream); int get ...

  2. C语言清空输入缓冲区的N种方法对比【转】

    转自:http://www.cnblogs.com/codingmylife/archive/2010/04/18/1714954.html C语言中有几个基本输入函数: //获取字符系列 int f ...

  3. C语言清空输入缓冲区的N种方法对比(转)

    C语言中有几个基本输入函数: //获取字符系列 int fgetc(FILE *stream); int getc(FILE *stream); int getchar(void); //获取行系列 ...

  4. c++中清空输入缓冲区的方法(做cf的时候炸了)

    C/C++ 四种清空输入缓冲区的方法 比较实用的一种 char c; while(c=getchar()!='\n'); 或者是这种 cin.ignore(count,c); count代表要清除的字 ...

  5. vs2019清空输入缓冲区

    发现用cin.sync()在vs2019中不能清空输入缓冲区,以前的vs版本没试过,我看别人在vc中用cin.sync()可以清除,估计是IDE的问题..以下是我学习C++四个多月写的一整段代码 运行 ...

  6. 解压tar.gz文件报错gzip: stdin: not in gzip format解决方法

    解压tar.gz文件报错gzip: stdin: not in gzip format解决方法 在解压tar.gz文件的时候报错 1 2 3 4 5 [Sun@localhost Downloads] ...

  7. C语言清空输入缓冲区

    来源:http://blog.csdn.net/guanyasu/article/details/53153705 https://zhidao.baidu.com/question/5241738. ...

  8. [转][修]C清空输入缓冲区

    为何要清空输入缓存区     读取时输入缓冲区中的内容会被scanf函数逐个取走,正常case下scanf()函数可以根据返回值判断成功取走的数目:但当发生读取异常之后,输入缓冲区中的内容并未被取走, ...

  9. dedecms清空栏目后,新建ID不从1开始的解决方法

    在后台SQL运行器运行下面的语句,这样新建的栏目ID就从1开始了: ALTER TABLE `dede_arctype` AUTO_INCREMENT =1; (注意表名) 下面是文章的,运行后,发布 ...

随机推荐

  1. 详细分析链表中的递归性质(Java 实现)

    目录 链表中的递归性质 前言 LeetCode 上关于链表的一道问题 203 号题目 移除链表中的元素 递归的基本概念与示例 链表天然的递归性 小结 链表中的递归性质 前言 在前面的 链表的数据结构的 ...

  2. 下拉列表被flash覆盖的解决方法

    做鼎闻有一段时间了,有的banner轮播图的地方用flash替换的时候,就会导致上面的导航条下拉列表被flash覆盖,找了一段时间没有得到有效的解决方法,后来知道关键是flash的这一属性{ &quo ...

  3. 11 vue 自定义全局方法

    //global.js// 定义vue 全局方   // 定义vue 全局方法 建议自定义的全局方法加_ 以示区分 export default {   install(Vue, options =  ...

  4. Oracle适配问题解决

    问题一:SQL 命令未正确结束 问题二:ORA-00907: 缺失右括号 问题三:mysql函数在Oracle中不适用 问题四:ORA-00936: 缺失表达式 问题五:No serializer f ...

  5. basicInterpreter1.02 增加对for循环的支持

    源码下载:https://files.cnblogs.com/files/heyang78/basicInterpreter102-20200531-2.rar 输入: for x= to print ...

  6. 同样是logback1.11,更换了log配置后,无论是否有线程持续不断写入log文件,log文件会按设定以日期序号轮换

    上次发现了logback1.11的一个bug,即有线程持续写入log,则log文件不会按设定模式进行轮换. 但发现同样采用logback1.11的另外一个工程,它的日志文件就没有错误,于是参照其配置文 ...

  7. PHP复杂变量

    eval($str="${${phpinfo()}}";)     →   可以执行phpinfo() ${phpinfo()} = {${phpindo()}} PHP复杂变量 ...

  8. Spring Boot与日志

    目录 1.日志框架 2.市面上的日志框架 2.1 下表行间无任何对应关系 2.2 日志门面:slf4j 2.3 日志实现:logback 2.4 Spring Boot怎么做的呢? 3.slf4j的使 ...

  9. 面试:为了进阿里,重新翻阅了Volatile与Synchronized

    该系列文章收录在公众号[Ccww技术博客],原创技术文章早于博客推出 在深入理解使用Volatile与Synchronized时,应该先理解明白Java内存模型 (Java Memory Model, ...

  10. pip更新命令

    python -m pip install --upgrade pip 更新时如果报错'NoneType' object has no attribute 'bytes', 解决办法:easy_ins ...