缓冲区的优点很明显,它加快了程序的运行速度,减少了硬件的读写次数,让整个计算机变得流畅起来;但是,缓冲区也带来了一些负面影响,经过前面几节的学习相信读者也见识到了。

那么,该如何消除这些负面影响呢?思路其实也很简单,在输入输出之前清空(刷新)缓冲区即可:

  • 对于输出操作,清空缓冲区会使得缓冲区中的所有数据立即显示到屏幕上;很明显,这些数据没有地方存放了,只能输出了。
  • 对于输入操作,清空缓冲区就是丢弃残留字符,让程序直接等待用户输入,避免引发奇怪的行为。

本节的代码用到了一些暂时没有学到的知识,估计很多初学者会不理解,没关系,那就先记住吧,记不住就复制吧,总之,按照葫芦画瓢就完了。

清空输出缓冲区

清空输出缓冲区很简单,使用下面的语句即可:

fflush(stdout);

fflush() 是一个专门用来清空缓冲区的函数,stdout 是 standard output 的缩写,表示标准输出设备,也即显示器。整个语句的意思是,清空标准输出缓冲区,或者说清空显示器的缓冲区。

Windows 平台下的 printf()、puts()、putchar() 等输出函数都是不带缓冲区的,所以不用清空,下面的代码演示了如何在 Linux 和 Mac OS 平台下清空缓冲区:

  1. #include<stdio.h>
  2. #include<unistd.h>
  3. int main()
  4. {
  5. printf("C语言中文网");
  6. fflush(stdout); //本次输出结束后立即清空缓冲区
  7. sleep(5);
  8. printf("http://c.biancheng.net\n");
  9. return 0;
  10. }

程序运行后,第一个 pirntf() 立即输出,等待 5 秒以后,第二个 printf() 才输出,这就符合我们的惯性思维了。如果不加fflush(stdout)语句,程序运行后,第一个 printf() 并不会立即输出,而是等待 5 秒以后和第二个 scanf() 一起输出(已在《C语言数据输出大汇总以及轻量进阶》中进行了演示),这有点不符合我们的思维习惯。

清空输入缓冲区

首先,很遗憾地说,没有一种既简洁明了又适用于所有平台的清空输入缓冲区的方案。只有一种很蹩脚的方案能适用于所有平台,那就是将输入缓冲区中的数据都读取出来,但是却不使用。

大家不要以为我很轻松地就能说出这句话,我FQ查阅了很多英文资料,又测试了很多平台和编译器才敢说的。

我们先说两种通用的方案,虽然它很蹩脚,但是却行之有效。

1) 使用 getchar() 清空缓冲区

getchar() 是带有缓冲区的,每次从缓冲区中读取一个字符,包括空格、制表符、换行符等空白符,只要我们让 getchar() 不停地读取,直到读完缓冲区中的所有字符,就能达到清空缓冲区的效果。请看下面的代码:

  1. int c;
  2. while((c = getchar()) != '\n' && c != EOF);

该代码不停地使用 getchar() 获取缓冲区中的字符,直到遇见换行符\n或者到达文件结尾才停止。由于大家所学知识不足,这段代码暂时无法理解,我也就不细说了,在实际开发中,大家按照下面的形式使用即可:

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int a = 1, b = 2;
  5. char c;
  6. scanf("a=%d", &a);
  7. while((c = getchar()) != '\n' && c != EOF); //在下次读取前清空缓冲区
  8. scanf("b=%d", &b);
  9. printf("a=%d, b=%d\n", a, b);
  10. return 0;
  11. }

输入示例:

a=100
b=200
a=100, b=200

按下第一个回车键后,只有第一个 scanf() 读取成功了,第二个 scanf() 并没有开始读取,等我们再次输入并按下回车键后,第二个 scanf() 才开始读取,这就符合我们的操作习惯了。如果没有清空缓冲区的语句,按下第一个回车键后,两个 scanf() 都读取了,只是第二个 scanf() 读取失败了,让人觉得很怪异,这点已在《使用scanf从键盘输入数据》中进行了演示。

改变输入方式,再次尝试一下:

a=100b=200
b=300
a=100, b=300

你看,第一次输入的多余内容并没有起作用,就是因为它们在第二个 scanf() 之前被清空了。

这种方案的关键之处在于,getchar() 是带有缓冲区的,并且一切字符通吃,或者说一切字符都会读取,不会忽略。不过这种方案有个缺点,就是要额外定义一个变量 c,对于有强迫症的读者来说可能有点难受。

2) 使用 scanf() 清空缓冲区

scanf() 还有一种高级用法,就是使用类似于正则表达式的通配符,这样它就可以读取所有的字符了,包括空格、换行符、制表符等空白符,不会再忽略它们了。并且,scanf() 还允许把读取到的数据直接丢弃,不用赋值给变量。

请看下面的语句:

scanf("%*[^\n]"); scanf("%*c");

第一个 scanf() 将逐个读取缓冲区中\n之前的其它字符,% 后面的 * 表示将读取的这些字符丢弃,遇到\n字符时便停止读取。此时,缓冲区中尚有一个\n遗留,第二个 scanf() 再将这个\n读取并丢弃,这里的星号和第一个 scanf() 的星号作用相同。由于所有从键盘的输入都是以回车结束的,而回车会产生一个\n字符,所以将\n连同它之前的字符全部读取并丢弃之后,也就相当于清除了输入缓冲区。

相信很多读者都不明白这种写法,没关系,下节我们在讲解 scanf() 的高级用法时还会再解释。

我们来演示这种方案的效果:

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int a = 1, b = 2;
  5. scanf("a=%d", &a);
  6. scanf("%*[^\n]"); scanf("%*c"); //在下次读取前清空缓冲区
  7. scanf("b=%d", &b);
  8. printf("a=%d, b=%d\n", a, b);
  9. return 0;
  10. }

输入示例 ①:

a=100
b=200
a=100, b=200

输入示例 ②:

a=100b=200
b=300
a=100, b=300

相比使用 getchar(),这种方案不用额外定义一个变量,看起来更加整洁。

两种不通用、不建议的方案

以上两种清空输入缓冲区的方案是通用的,在任何平台、任何编译器、任何情景下都奏效。除此以外,有些教材和老师可能还讲解过其它的方案,常见的有两种,分别是fflush(stdin)rewind(stdin)

1) fflush(stdin)

fflush(stdin) 常用于 Windows 平台,在 VC 6.0、VS2010 等较老的编译器下确实能够清空缓冲区。

C语言标准规定,当 fflush() 用于 stdout 时,必须要有清空输出缓冲区的作用;但是C语言标准并没有规定 fflush() 用于 stdin 时的作用,编译器的实现者可以自由决定,所以它的行为是未定义的。

较老的微软编译器进行了扩展,赋予了 fflush(stdin) 清空输入缓冲区的功能,例如 VC 6.0、VS2010 等;但是,较新的微软编译器又取消了这种扩展,不再支持 fflush(stdin),例如 VS2015、VS2017 等,在这些版本的编译器下,fflush() 是无效的。

较老的 GCC 是不支持 fflush(stdin) 的,但是最新的 GCC 又开始支持 fflush(stdin) 了。

LLVM/Clang 编译器始终不支持 fflush(stdin)。

总之,fflush(stdin) 这种不标准的写法只适用于一部分编译器,通用性非常差,所以不建议使用。如果你由于个人习惯坚持使用,请测试你的编译器是否支持。

2) rewind(stdin)

rewind() 函数并没有清空缓冲区的功能,但是 rewind(stdin) 偏偏在某些编译器下会导致清空缓冲区的假象,例如 VS2015、LLVM/Clang。在 GCC 下,rewind(stdin) 是没有任何效果的。

总结

最靠谱、最通用、最有效的清空输入缓冲区的方案就是使用 getchar() 或者 scanf() 将缓冲区中的数据逐个读取出来,其它方案都有或多或少的问题。

C语言:清空缓冲区的更多相关文章

  1. C++中清空缓冲区

    C++中标准输入cin有多种输入方式.. 这篇文章罗列的还是简要易懂的.C++输入cin详解...如果只是简单的使用cin>>的话,会单个token的读入.但是会忽略换行符,空格,制表符等 ...

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

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

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

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

  4. C语言:缓冲区

    缓冲区(Buffer)又称为缓存(Cache),是内存空间的一部分.也就是说,计算机在内存中预留了一定的存储空间,用来暂时保存输入或输出的数据,这部分预留的空间就叫做缓冲区(缓存).有时候,从键盘输入 ...

  5. gcc c语言中scanf输入格式不正确,清空缓冲区问题

    我的博客:www.while0.com 折磨了一下午,只因为fflush(stdin)再gcc里和vc里表现不一致.gcc里不能够清空缓冲区.直接上例子: #include <stdio.h&g ...

  6. scanf_s获取参数,清空缓冲区,判断是否读取成功

    #include<stdio.h> int main() { ]; ) { printf("Please input:\n"); ); ) { printf(" ...

  7. 用 getchar putchar 来输入和接收 但是要清空缓冲区

    1 //用 getchar putchar 来输入和接收 但是要清空缓冲区 2 3 #include <stdio.h> 4 int main() 5 { 6 char ch1,ch2; ...

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

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

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

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

随机推荐

  1. 死磕nginx系列

    死磕nginx系列 死磕nginx系列--nginx入门 死磕nginx系列--nginx配置文件 死磕nginx系统--nginx日志配置 死磕nginx系列--nginx服务器做web服务器 死磕 ...

  2. GPU编程和流式多处理器(七)

    6. 杂项说明 6.1. warp级原语 warp作为执行的原始单元(自然位于线程和块之间),重要性对CUDA程序员显而易见.从SM 1.x开始,NVIDIA开始添加专门针对thread的指令. Vo ...

  3. 高效Tensor张量生成

    高效Tensor张量生成 Efficient Tensor Creation 从C++中的Excel数据中创建Tensor张量的方法有很多种,在简单性和性能之间都有不同的折衷.本文讨论了一些方法及其权 ...

  4. SpringBoot系列——cache缓存

    前言 日常开发中,缓存是解决数据库压力的一种方案,通常用于频繁查询的数据,例如新闻中的热点新闻,本文记录springboot中使用cache缓存. 官方文档介绍:https://docs.spring ...

  5. 看懂redis配置文件

    看懂redis 配置文件: https://blog.csdn.net/liqingtx/article/details/60330555 redis 数据库缓存双写一致性解决方案: https:// ...

  6. Spring Cloud09: Config 配置中心

    一.概述 什么是配置中心呢,在基于微服务的分布式系统中,每个业务模块都可以拆分成独立自主的服务,由多个请求来协助完成某个需求,那么在某一具体的业务场景中,某一个请求需要调用多个服务来完成,那么就存在一 ...

  7. 「题解」agc031_e Snuke the Phantom Thief

    本文将同步发布于: 洛谷博客: csdn: 博客园: 简书. 题目 题目链接:洛谷 AT4695.AtCoder agc031_e. 题意简述 在二维平面上,有 \(n\) 颗珠宝,第 \(i\) 颗 ...

  8. 面试常问的Java虚拟机内存模型,看这篇就够了!

    一.虚拟机 同样的java代码在不同平台生成的机器码肯定是不一样的,因为不同的操作系统底层的硬件指令集是不同的. 同一个java代码在windows上生成的机器码可能是0101.......,在lin ...

  9. 用好idea这几款插件,可以帮你少写30%的代码

    Easycode是idea的一个插件,可以直接对数据的表生成entity,controller,service,dao,mapper,无需任何编码,简单而强大. 1.安装(EasyCode) 我这里的 ...

  10. JNI相关笔记 [TOC]

    JNI相关笔记 目录 JNI相关笔记 1 生成native code所需要的头文件 2 JNI提供的一些函数和方法 3 局部引用,全局引用,全局弱引用. 4 异常 1 生成native code所需要 ...