(给CPP开发者加星标,提升C/C++技能)

作者:技术让梦想更伟大 / 李肖遥 (本文来自作者投稿)

前言

有没有发现,基本上所有的C语言入门书籍,或者是我们的教程里面,第一个C语言程序实体,都是“Hello World!”;我不知道这是不是行业的“潜规则”,总之,它把无数的程序员带进了计算机的世界,步入了代码的大坑里,所以你好,世界!

一件趣事

我记得大学学习计算机的时候,就是在电脑这样的一个程序,不知道经过了什么过程,就能在计算机上显示出"Hello World!"。后来我把这个"Hello World!"改成了"I Love xxx!",哦买噶,真是惊呆我了,有一种立马在女神面前炫耀的感觉了.

那么,这其中有什么奥妙呢,我们从C语言的输入输出流开始说起.

hello world 是怎么显示出来的

对的,就是这样的一个程序

#include 

在我们日常生活中,总看到电子显示屏上面显示着"欢迎某位领导莅临我校观察","宾馆"之类的,那是不是"Hello World!"也是这样编译出来的呢?

我们看到程序中的printf();由系统或者编译器提供商提供的一个应用接口,是格式化输出函数, 一般用于向标准输出设备按规定格式输出信息。一般其函数原型应该是这样的:

printf(const char *,...);
printf, (const char *__restrict, ...)

经过预处理,编译,汇编,链接四个过程,借助了相应的缓冲区来进行输入与输出,就会显示出来输入输出流

流是什么

“流”即是流动的意思,是物质从一处向另一处流动的过程,是对一种有序连续且具有方向性的数据的抽象描述。

在计算机系统中是指信息从外部输入设备向计算机内部输入,或者从内存向外部输出设备输出的过程。这种输入输出的过程被形象的比喻为“”。

输入输出

什么是输入输出呢?C语言中我们用到的最频繁的输入输出方式就是scanf()与printf()。

scanf():从标准输入设备(键盘)读取数据,并将值存放在变量中。

printf():将指定的文字/字符串输出到标准输出设备(屏幕)。注意宽度输出和精度输出控制。

字符输入输出(getchar/putchar),字符串输入输出函数 (gets与puts),与gets/puts类似的还有fgets与fputs,它们一般用于对文件的操作.

缓冲区

定义

缓冲区是内存空间的一部分,也就是说在内存空间中预留了一定大小的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,根据其对应的是输入设备还是输出设备,分为输入缓冲区输出缓冲区

原理介绍

当调用输入函数scanf()时,输入函数会将我们输入的数字输入到输入缓冲区,而当我们的输入缓冲区有内容时,再次输入将不会被执行,而是直接跳过执行,将输入缓冲区的内容赋给变量。

引入缓冲区的意义

缓冲区就是一块内存,用来做数据的一个临时存放点,在输入输出操作中起着至关重要的作用,在百度百科定义如下

比如我想把一篇文章以字符序列的方式输出到计算机显示器屏幕上,那么我的程序内存作为数据源而显示器驱动程序作为数据目标,如果数据源直接对数据目标发送数据的话。数据目标获得第一个字符,便将它显示。然后从端口读取下一个字符,可是这时就不能保证数据源向端口发送的恰好是第二个字符(也许是第三个,而第二个已经在数据目标显示时发送过了)。这样的话就不能保证输出的数据能完整的被数据目标所接受并处理。

缓冲区的类型

缓冲区有三种,我一个一个地说下:

1、全缓冲

内存中有一段存储区域,比如有1024个字节大小,有一个程序会从这段存储区域中读取数据。现在系统把一个文件的内容放入这个存储区,只要1024个字节都放满了,那么程序会立即来读取这1024个字节的数据。只要1024个字节没有放满,哪怕只放了1023个字节,程序都不会来读取,这就是全缓冲的意思。

#include  

编译并执行,运行结果如下:

此时打开工程所在文件夹下的test.txt文件,您会发现该文件是空的,这说明4096个字符“a”还在缓冲区,并没有真正执行I/O操作。敲一下回车键,窗口变为如下:


此时再打开test.txt文件,您就会发下该文件中已经有了4096个字符“a”。这说明全缓冲区的大小是4K(4096),缓冲区满后执行了I/O操作,而字符“b”还在缓冲区。

再次敲一下回车键,窗口变为如下:  此时再打开test.txt文件,您就会发现字符“b”也在其中了。这一步验证了文件关闭时刷新了缓冲区。

2、行缓冲

内存中有一段存储区域,比如有1024个字节大小,有一个程序会从这段存储区域中读取数据。现在系统把一个文件的内容放入这个存储区,假如放了10个字节的数据,你敲了回车键,那么程序就马上来读取了。假如放了20个字节,你敲了回车奖,程序也会来读取。所以即使1024个字节没有放满,但是你敲了回车键,程序就会来读取,这个就叫做行缓冲。

使用键盘操作演示行缓冲,先介绍getchar()函数。函数原型:

int getchar(void) ;

说明:当程序调用getchar()函数时,程序就等着用户按键,用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符也放在缓冲区中)。当用户键入回车之后,getchar()函数才开始从键盘缓冲区中每次读入一个字符。也就是说,后续的getchar()函数调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才重新等待用户按键。

#include 

最后输出结果是

a ,b ,c ,d ,e ,f可以交替按下一些字符,编译结果如下:

当按到第4096个字符时,提示您不能再按下去,说明行缓存的大小是4k,此时按下回车键,返回第一个字符是‘a’继续敲下回车键,缓存区的其他字符就全部输出


3、无缓冲

内存中有一段存储区域,比如有1024个字节大小,有一个程序会从这段存储区域中读取数据。现在系统把一个文件的内容放入这个存储区,刚放了1个字节,程序就马上来读取了;又放了一个字节,程序又马上来读取了,这就是没有缓冲。

在C语言中,一般规定是要有行缓冲的。但是使用scanf函数和getchar时,如果行缓冲的换行符没有处理好,程序运行可能会有异常或者闪退等现象。也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。

如错误输出时使用:

cerr<

这条语句等效于:

fprintf(stderr, ”错误,请检查输入的参数!”) ;

缓冲区的大小

如果我们没有自己设置缓冲区的话,系统会默认为标准输入输出设置一个缓冲区,这个缓冲区的大小通常是 512个字节 的大小。

缓冲区大小由 stdio.h 头文件中的宏 BUFSIZ 定义,如果希望查看它的大小,包含头文件,直接输出它的值即可:

printf(

缓冲区的大小是可以改变的,也可以将文件关联到自定义的缓冲区,详情可以查看 setvbuf()和 setbuf() 函数。

缓冲区的刷新

下列情况会引发缓冲区的刷新:

  • 缓冲区满时;
  • 执行flush语句,即使用特定函数刷新缓冲区;
  • 执行endl语句,即行缓冲区遇到回车时;
  • 关闭文件。

可见,缓冲区满或关闭文件时都会刷新缓冲区,进行真正的I/O操作。另外,在C++中,我们可以使用flush函数来刷新缓冲区(执行I/O操作并清空缓冲区) 如:

cout <

endl控制符的作用是将光标移动到输出设备中下一行开头处,并且清空缓冲区。

cout 

相当于

cout 

强制缓冲区的数字打印

/* 输出缓冲区演示 */ #include int main(){     printf("1\n");     fflush(stdout); //强制将输出缓冲区的内容显示在屏幕上     while (1){    }     return 0; }

如何清空输入缓冲区的内容?

如果我想让getchar()每次都能够等待用户输入的话就要清空缓冲区,下面就介绍不同平台的方法

C标准规定 fflush()函数是用来刷新输出(stdout)缓存的。对于输入(stdin),它是没有定义的。但是有些编译器也定义了 fflush( stdin )的实现,比如微软的VC。其它编译器是否也定义了 fflush( stdin )的实现应当查找它的手册。GCC编译器没有定义它的实现,所以不能使用 fflush( stdin )来刷新输入缓存。

对于没有定义 fflush( stdin )的编译器,可以使用 fgets()函数来代替它(比用 getchar()、scanf()等函数通用性好)。可以这样忽略输入流中留下的回车等其它输入,从而使下一次的输入总是保持一个“干净”的状态。(这个是任何平台下都可以的)

char sbuf[1024];// ...
fgets( sbuf, 1024, stdin );// ...

在windows 的vc下面就可以这样了:

for(int i=0;i<10;++i)

这里说到gcc编译器没有定义fflush的实现,我们一般用getchar();来清除缓冲区

#include 

输入:ssss回车得到:ssss,光标处(等待输入)

说明: 此时程序没有结束,进行到for循环,因为并没有字符a出现,所以还没跳出for循环.键入回车后,getchar依次从缓冲区内取出(for循环):'s''s''s''s''\n'

如果我们输入:ssssa回车得到:ssssa,光标处(等待输入)

说明: 程序已经跳出for循环,但是由于我们用getchar();清除了换行'\n',后面第7句c=getchar();需要你输入一个字符(因为ssssa后面并没有新的字符),所以程序仍然没有结束.

如果我们注释掉getchar();这一句,那么得到:ssss,光标处(程序结束) 这个输入ssssa是的回车中的换行符'\n'就被c=getchar();这一句读取并输出了。

总结:键盘输入的字符都存到缓冲区内,一旦键入回车,getchar就进入缓冲区读取字符,一次只返回第一个字符作为getchar函数的值,如果有循环或足够多的getchar语句,就会依次读出缓冲区内的所有字符直到'\n'.

要理解这一点,之所以你输入的一系列字符被依次读出来,是因为循环的作用使得反复利用getchar在缓冲区里读取字符,而不是ge

最后

很多表面的现象看起来可能不能引起我们的注意,但是当我们注意到细节的时候,才能深究其中的奥秘,写代码亦是如此。

- EOF -

推荐阅读  点击标题可跳转

1、真的可以,用 C 语言实现面向对象编程 OOP

2、C语言与C++面试知识总结

3、一文读懂 C 语言与 C++ 动态内存

看完本文有帮助?请分享给更多人

关注「CPP开发者」加星标,提升C/C++技能

好文章,我在看❤️

au加载默认的输入和输出设备失败_一文带你读懂 C/C++ 语言输入输出流与缓存区...相关推荐

  1. 优先队列默认是小顶堆吗_一分钟带你读懂什么是堆?

    堆其实就是一种特殊的队列--优先队列. 普通的队列游戏规则很简单:就是先进先出:但这种优先队列搞特殊,不是按照进队列的时间顺序,而是按照每个元素的优先级来比拼,优先级高的在堆顶. 这也很容易理解吧,比 ...

  2. au加载默认的输入和输出设备失败_Mac OS X的音频输入输出时如何调整音量

    本次为大家介绍一款音频管理软件,AudioSwitcher for Mac是一款位于菜单栏的音源设备管理工具.利用这款软件能够帮大家设定音频输入设备和音频输出设备,从而帮助大家迅速调整输入设备的音量以 ...

  3. 图片加载失败后---加载默认图片

    一 . 图片加载失败原因: 文件路径不正确: 标签属性等元素错误: 错误的文件扩展名 文件丢失 存放位置出现问题 网络传输问题: 二 . 加载失败后页面默认样式 如下 ↓: 三 . 封装函数:图片加载 ...

  4. Launcher3 workspace 加载默认的布局

    本文主要分析launcher3 如何加载默认的icon ,基于AOSP Q Launcher3 code 1. launcher首先会初始化设备配置属性,比如加载哪些图标,图标布局n * n等等,这些 ...

  5. vue + 图片加载 + 默认图片

    需求: 图片数据未返回 + 图片数据返回为空 + 图片加载出错 时,图片采用默认图片展示. 解决: 先判断返回数据是否有,再通过img 的 load事件判断是否加载完成. 现象: 图片会先加载默认图片 ...

  6. easyui combogrid分页加载默认值时无法自动跳转到相应页的解决方案

    2019独角兽企业重金招聘Python工程师标准>>> 当如果easyui combogrid分布加载时,有默认值,且默认值不在第一页时,此时combogrid里的显示值为idFie ...

  7. 设置afni环境变量—打开afni加载默认的大脑模板

    打开afni的默认安装目录-/abin,里面有个对afni环境变量描述的文件README.environment.根据该文件对环境变量的功能描述,可以设置各种环境变量使得afni的操作简洁. 举个例子 ...

  8. vue el-tree懒加载默认展开一级,懒加载刷新(默认高亮某个节点,触发某个节点的点击事件)

    场景 如上图所示,左边展示分组及分组下的标签,点击某个标签,在右边展示某个标签的详情,可以对标签内容进行编辑保存,还可对标签进行搜索,默认展示全部标签,展开一级不目下标签,若对标签进行搜索筛选,则把每 ...

  9. 加载默认图片,如何避免img标签陷入onerror事件死循环

    当图片加载失败的时候,我们可以利用onerror事件赋予它默认图片,但是问题来了,假如默认图片又不存在呢,即加载失败,这个时候就会陷入死循环. 为了避免死循环的情况,我们可以在执行完onerror事件 ...

最新文章

  1. 全球及中国太阳能硅片产业供需走势及投资建设前景分析报告2021-2027年
  2. spring boot 整合redis实现方法缓存
  3. mysql guid_MySQL – 自动增加到guid
  4. Java更改中文_Java中文编程开发,让Java编写更改复杂
  5. mysql orm c语言_【译】Simple MySQL ORM for C
  6. 数据结构之线性表:顺序线性表 Java 实现(底层基于数组)
  7. CCTextureCache的多线程加载原理和使用
  8. Python自动对Word文件中Python程序进行着色
  9. 2018年,加油!!!
  10. python三本经典书籍-《python编程入门经典》python之父推荐这三本书让你更快入门...
  11. ASP.NET中的图片缓存
  12. mysql中getdate怎么用_SQL中 getdate() 用法
  13. 小米怎么解锁,有什么相关教程
  14. Chrome前端插件
  15. php安装失败,phpcms安装失败怎么办
  16. 男人,你连剃须刀都不知道怎么选,活该你单身!
  17. 【先进事迹】11111HTML
  18. C#输入三条边判断是否为三角形
  19. 发展最快的小程序:甩甩宝宝
  20. 淘宝官方集运(大陆发货,收货地台湾)流程

热门文章

  1. 3DSlicer9:FAQ-3
  2. 大数据可视化软件//完美的SCI配图
  3. javascript中的try finally
  4. 23种设计模式C++源码与UML实现--责任链模式
  5. vim中使用split
  6. BUUCTF-Reverse:xor(涉及异或脚本编写)
  7. Vue 里 几个重要的指令
  8. 输入10个数,根据提示进行从小到大输出或从大到小输出
  9. html中调用flex中的函数
  10. 任务切换的方法——《x86汇编语言:从实模式到保护模式》读书笔记37