一、标准IO的缓冲区
1.1 数据流动流程图

缓存区结束地址减去缓存区起始地址等于缓存区的内存大小
bash
stderr->_IO_buf_end - stderr->_IO_buf_base;
1.2 标准 I/O 缓冲区策略
定义:标准 I/O 中的缓冲区是C语言标准库为优化文件读写效率设计的内存机制,
本质上就是一段由FILE结构体管理的内存空间;
作用:通过临时存储数据、批量处理,可以减少硬件IO (如磁盘、终端)的交互次数,提升程序效率;
| 缓冲类型 | 默认应用场景 | 缓冲区大小(Linux) | 特点 |
|---|---|---|---|
| 行缓冲 | 终端设备 (如 stdout、stdin) |
1024 字节(1KB) | 遇到换行符或缓冲区满 时刷新,适合交互式设备 |
| 全缓冲 | 普通文件 (如 .txt) |
4096 字节(4KB) | 缓冲区满或手动刷新时 才写入,适合文件读写 |
| 无缓冲 | 标准错误 (stderr) |
0 字节 | 数据不暂存,直接写入硬件设备 ,适合立即输出错误信息 |
占位符是%ld
只有在使用缓存区的才会分配大小 ,不使用的时候大小为0;
行缓存只有交互的时候才会分配空间 ,全缓存只有读写操作的时候才会分配空间
二、缓冲区刷新时机
2.1 行缓冲刷新时机
| 刷新时机 | 说明 |
|---|---|
遇到换行符 \n |
输出换行时自动刷新缓冲区 |
| 程序正常结束 | main 函数返回或调用 exit() 时刷新 |
| 文件关闭 | 调用 fclose() 关闭文件时刷新 |
| 输入/输出切换 | 从输出状态切换到输入状态 时(如 scanf 前)自动刷新 |
| 缓冲区写满 | 缓冲区已满但未遇到换行符时强制刷新 |
主动调用 fflush() |
手动调用 fflush(fp) 强制刷新指定缓冲区 |
注:
循环本身并不产生数据 。如果在刷新条件触发之前程序陷入了死循环,那么之前输出的内容会一直卡在缓冲区里,永远不会显示。
只有在循环体内反复调用 printf 等输出函数,才会向缓冲区填充数据。
缓冲区溢出并不会"自动刷新",而是会导致程序崩溃。
当**程序不断向缓冲区写入字符,直到写满1024字节,这时无论是否遇到换行符,缓冲区都会自动刷新。**这是那六个条件中的"缓冲区写满"。
2.2 fflush函数
| 项目 | 内容 |
|---|---|
| 所需头文件 | #include <stdio.h> |
| 函数原型 | int fflush(FILE *stream); |
| 功能 | 将指定流(stream)缓冲区中尚未写入的内容 ,强制写入到对应的输出设备(如文件、终端等) |
| 参数 | stream ------ 用于指定需要刷新缓冲区的流 |
| 成功返回值 | **0(**刷新了流缓冲区) |
| 失败返回值 | EOF,并将 errno 设置为具体的错误类型 |
2.3 全缓冲刷新时机
| 刷新时机 | 说明 |
|---|---|
| 程序正常结束 | main 函数返回或调用 exit() 时刷新缓冲区 |
| 文件关闭 | 调用 fclose() 关闭文件时刷新缓冲区 |
| 输入/输出切换 | 从输出状态切换到输入状态时自动刷新缓冲区 |
| 缓冲区写满 | **缓冲区已满(未遇到换行符)**时强制刷新 |
主动调用 fflush() |
手动调用 fflush(fp) 强制刷新指定缓冲区 |
与行缓冲刷新时机区别是遇到/n不会刷新缓存区
三、手动设置缓冲区
3.1 手动设置缓冲
| 分类 | 说明 |
|---|---|
| 覆盖默认缓冲策略 | 不同系统/语言对 IO 流的默认缓冲设置不同(如 C 语言中 stdout 默认行缓冲,stderr 默认无缓冲),手动设置可按需调整 |
| 性能与实时性平衡 | • 行缓冲 :适合交互式输出(如命令行程序),每行输出后自动刷新 • 全缓冲 :适合大量数据批量写入(如日志文件),减少 IO 次数 • 无缓冲:适合需要立即显示的关键信息(如错误日志) |
| 特殊场景需求 | 例如网络编程中避免数据堆积,或调试时强制输出中间结果 |
| 日志系统 | 根据日志级别设置缓冲:错误日志无缓冲,普通日志全缓冲 |
| 交互式工具 | 命令行程序中确保用户输入后立即响应 |
| 嵌入式系统 | 受限内存环境下自定义缓冲区大小 |
3.2 setbuf函数
| 项目 | 内容 |
|---|---|
| 所需头文件 | #include <stdio.h> |
| 函数原型 | void setbuf(FILE *stream, char *buf); |
| 功能 | 设置指定文件流的缓冲区 |
| 参数 - stream | 指向 FILE 对象的指针,标识要设置缓冲区的文件流 |
| 参数 - buf | 指向用户提供的缓冲区的指针 • 如果提供了缓冲区,其大小必须至少为 BUFSIZ 字节(通常在 stdio.h 中定义) • 如果为 NULL,则是无缓冲模式 |
| 返回值 | 无 |
| 分类 | 说明 |
|---|---|
| 全缓冲模式 | 如果 buf 指向一个大小大于 0 的缓冲区,那么 IO 操作将使用全缓冲模式 |
| 无缓冲模式 | 如果 buf 为 NULL,或者是大小为 0,那么 IO 操作将变为无缓冲模式 |
| 调用时机 | 应该在打开文件流之后、任何 IO 操作之前调用,否则可能不会生效 |
| 缓冲区大小 | 使用自定义缓冲区时,确保缓冲区足够大,以避免缓冲区溢出 |
| 生命周期管理 | 使用自定义缓冲区时,不要在 buf 指向的缓冲区被释放后再进行 IO 操作,以免导致未定义的行为 |
3.3 sleep函数
| 项目 | 内容 |
|---|---|
| 所需头文件 | #include <unistd.h> |
| 函数原型 | unsigned int sleep(unsigned int seconds); |
| 功能 | 用于让程序暂停执行指定的时间 |
| 参数 - seconds | 指定程序暂停执行的秒数 |
| 成功返回值 | 0(表示休眠了完整的指定秒数) |
| 失败返回值 | 若程序被信号中断,返回未休眠的秒数(即剩余未休眠完的秒数) |
3.4 setvbuf函数
| 项目 | 内容 |
|---|---|
| 所需头文件 | #include <stdio.h> |
| 函数原型 | int setvbuf(FILE *stream, char *buf, int mode, size_t size); |
| 功能 | 更加灵活地控制文件流的缓冲行为,允许指定缓冲模式和缓冲区的大小。 注意 :该函数必须在对 stream 执行任何其他操作前调用 |
| 参数 - stream | 指向 FILE 对象的指针,标识要设置缓冲区的文件流 |
| 参数 - buf | 指向用户提供的缓冲区的指针 • 如果为 NULL,函数会自动分配一个大小为 size 的缓冲区 |
| 参数 - mode | 缓冲模式,必须是以下之一: • _IOFBF:全缓冲 • _IOLBF:行缓冲 • _IONBF:无缓冲(当 mode 为 _IONBF 时,size 参数被忽略) |
| 参数 - size | 缓冲区的大小(以字节为单位) |
| 成功返回值 | 0 |
| 失败返回值 | 非 0 值 |
注意事项
| 分类 | 注意事项 |
|---|---|
| 调用时机 | setvbuf 必须在文件流执行任何读写操作之前调用(文件流初始化后),否则可能导致设置失效或触发未定义行为 |
自定义缓冲区大小(buf != NULL) |
需确保缓冲区大小 size 与实际需求匹配: • size 过小:可能导致数据溢出 • size 过大:会浪费内存资源 |
自动分配缓冲区(buf = NULL) |
size 需设置合理值(通常建议不小于系统默认缓冲区大小,如 BUFSIZ),避免因缓冲区过小影响性能或触发溢出 |
| 缓冲区生命周期 | 禁止在缓冲区被释放后对文件流执行 IO 操作,否则会触发未定义行为(如访问已释放内存导致崩溃) |
无缓冲模式(_IONBF) |
当 mode 为 _IONBF(无缓冲)时,size 参数会被忽略(即使设置非零值也无效),需避免依赖 size 配置无缓冲模式 |
| 平台兼容性 | 不同系统对 mode 的支持可能有差异(如某些嵌入式环境可能不支持行缓冲 _IOLBF),需根据实际平台验证行为 |