缓冲区
该笔记适用于Ubuntu,在Windows或其它平台上可能会存在不同。
什么是缓冲?
缓冲区又称为缓存,它是内存空间的一部分,用来缓冲输入或输出的数据。
为什么要引入缓冲区呢?
比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作远快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。
缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来缓存数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。
缓冲区的类型
缓冲区分为三种类型:全缓冲、行缓冲和不缓冲。
1、全缓冲
在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
2、行缓冲
在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。
3、不缓冲
也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
缓冲区的刷新
下列情况会引发缓冲区的刷新:
-
缓冲区满时;
-
执行fflush函数;
-
正常关闭文件。
-
正常退出程序或进程。
-
行缓冲遇到 \n刷新。
如何改变缓存类型
这里只展示setvbuf函数的使用,定义流stream应如何缓冲。
//与缓冲区相关的一些函数
#include <stdio.h>
void setbuf(FILE *stream, char *buf);
void setbuffer(FILE *stream, char *buf, size_t size);
void setlinebuf(FILE *stream);
int setvbuf(FILE *stream, char *buf, int mode , size_t size);
参数:
stream:这是指向 FILE 对象的指针,该 FILE 对象标识了一个打开的流。
buffer:这是分配给用户的缓冲。如果设置为 NULL,该函数会自动分配一个指定大小的缓冲。
mode: 这指定了文件缓冲的模式:_IOFBF、_IOLBF、_IONBF。
size:这是缓冲的大小,以字节为单位。
返回值:
如果成功,则该函数返回 0,否则返回非零值。
模式 | 描述 |
---|---|
_IOFBF | 全缓冲:对于输出,数据在缓冲填满时被一次性写入。对于输入,缓冲会在请求输入且缓冲为空时被填充。 |
_IOLBF | 行缓冲:对于输出,数据在遇到换行符或者在缓冲填满时被写入,具体视情况而定。对于输入,缓冲会在请求输入且缓冲为空时被填充,直到遇到下一个换行符。 |
_IONBF | 无缓冲:不使用缓冲。每个 I/O 操作都被即时写入。buffer 和 size 参数被忽略,即在无缓存模式,即使设置了缓冲区也不会用到。 |
验证
先来对比VScode 和Ubun中缓冲的表现形式
2 .
(截图有点多😂,主要为了我以后复习方便一点,望见谅)
从以上10个测试可以得到结论:(不同平台不同的编译器得到的结果可能不一样)
- VScode中的stdout属于无缓冲类型
- Ubuntu中的stdout属于行缓冲类型(行缓冲遇到换行符会清空缓冲区,而全缓冲不会)
注意:缓冲区的大小选择1024(不管用默认的还是自己定义的),我试过用100字节大小的缓冲区,导致结果不一样,具体情况可能与stdout内部的实现规则有关,知道原因的朋友欢迎留言。
以下结论测试截图就不发出来了,感兴趣的可以自己测一下
-
当按下Ctrl+C强行中断程序时,VScode会将缓冲区中的剩余内容刷出,而Ubuntu不会;
比如在Ubuntu中中打开文件进行写数据时(存在缓冲区的情况),如果中途强行Ctrl +C,由于没有关闭文件而且Ubuntu不会刷新缓冲区,会导致尚在缓冲区中的内容没有写入到文件中;而在VScode上,强行终端后输入的数据还是会写入到文件中。
-
printf函数在Ubuntu的特点(VScode没有)(仅仅是对于行缓冲):
-
遇到换行符时会刷新缓冲区
-
遇到getchar()、scanf()这种需要输入数据的函数时会刷新缓冲区
-
验证setvbuf函数
- 当缓冲区满的时候会刷新缓冲区
- 当设为无缓冲时,所设置的缓冲区会被忽略
由于还不清楚stdout的源码是如何实现功能的,在VS和Ubuntu上验证缓冲区满是刷新缓冲区输出的内容方面还存在一些疑问,上面的截图只是刚好满足而已,后续如果了解原因会进行更新。
参考资料
>>缓冲区与缓存
>>什么是缓冲区
其它笔记
>>变参函数