值得注意的是,标准I/O函数在进行操作时,先操作缓存区,当缓存区满足刷新条件时,再执行系统调用,实现缓存区与文件的数据交互。 Linux
中对刷新的概念有两层意思:在标准I/O库方面,刷新意味着将缓存中的内容写到磁盘上;在终端驱动程序方面,刷新表示丢弃已存在缓存中的数据。在这里,刷新通常指将缓存区清空,并将数据交互到文件或终端。
除不缓存外,全缓存和行缓存,都有自己刷新的条件。
刷新全缓存的条件是缓存区写满、强制刷新( fflush()
)、程序正常退出。而行缓存的刷新条件是缓存区写满、强制刷新( fflush()
)、程序正常退出、换行符'\n'
。
因此,操作流指针使用的缓存区大小是固定的。如果不喜欢这些系统默认,也可以通过函数修改指定流指针所操作的缓存区。
根据 FILE
结构体即可得到缓存区的大小,示例如下:
#include <stdio.h> int main(int argc, const char *argv[]) { FILE *fp; fp = fopen("test.txt", "w"); fputc('a', fp); printf("fp buffer size:%ld\n", fp->_IO_buf_end - fp->_IO_buf_base); getchar(); printf("stdin buffer size:%ld\n", stdin->_IO_buf_end - stdin->_IO_buf_base); printf("stdout buffer size:%ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base); printf("stderr buffer size:%ld\n", stderr->_IO_buf_end - stderr->_IO_buf_base); return 0; }点击复制复制失败已复制
编译运行,得到如下内容:
$ gcc main.c && ./a.out fp buffer size:4096 stdin buffer size:1024 stdout buffer size:1024 stderr buffer size:0点击复制复制失败已复制
提示
输出内容中间的空行为键盘输入的回车。
由上例可以看出,通过不同操作对象的流指针,访问其结构体成员,通过地址相减,得到缓存区的大小。其中 _IO_buf_end
和 _IO_buf_base
为缓存区结尾地址与缓存区起始地址。
fp
是与文件关联的流指针, stdin
、 stdout
、 stderr
都是与终端关联的流指针,其类型都为 FILE*
,已知结构体指针,可访问结构体成员。因此根据缓存区的定义,可以得到全缓存的大小为 4KB
,行缓存为 1KB
。
对任何一个给定的流,如果不喜欢这些系统默认,则可调用下列两个函数中的一个更改缓存类型。
#include <stdio.h> void setbuf(FILE *stream, char *buf); int setvbuf(FILE *stream, char *buf, int mode, size_t size);点击复制复制失败已复制
setvbuf()
函数可以通过 mode
参数精确设置所需的缓存类型。 _IOFBF
指定为全缓存, _IOLBF
指定为行缓存, _IONBF
指定为不缓存。如果指定一个不缓存的流,则忽略buf
和 size
参数。如果指定全缓存或行缓存,则 buf
和 size
可以选择指定一个缓存以及长度。如果指定流为待缓存的,而 buf
为 NULL
,则标准I/O库将自动地为该流分配适当长度的缓存。如果系统不能为该流决定此值,则分配长度为 BUFSIZ
(典型值为 1024
)的缓存。
函数 | mode | buf | 缓存及长度 | 缓存类型 |
setvbuf | _IOFBF | nonnull | 长度为size | 全缓存 |
NULL | 系统设定 | |||
_IOLBF | nonnull | 长度为size | 行缓存 | |
NULL | 系统设定 | |||
_IONBF | 忽略 | 无缓存 | 不缓存 | |
setbuf | nonull | 长度为BUFSIZ | 全缓存或行缓存 | |
NULL | 无缓存 | 不缓存 |