stdio.h的缓冲机制解析
1. 令人迷惑的printf()
在C语言中,由于stdio.h中的缓冲机制,printf的输出通常会受到缓冲区的影响。
这种影响可能非常微妙,并常常令人疑惑,比如我们来看下面这段代码
#include <stdio.h>
int main(void) {
printf("Hello World");
while(1);
}
在命令行中编译运行,发现他只是一味循环,输出不见了?!!
但是如果我们修改一下代码,添加一个换行符:
printf("Hello World\n");
就可以看到Hello World被输出了?!

2. stdio的缓冲机制解析
根据标准I/O的缓冲方式,printf的输出主要有以下几种情况:
2.1. 行缓冲(Line Buffering)
- 默认情况下,面向终端(标准输出/
stdout是终端)的文件流使用行缓冲。 - 缓冲区在以下情况下刷新:
- 输出了一个换行符
\n。 - 缓冲区被填满。
- 主动调用刷新函数(如
fflush(stdout))。 - 程序正常结束,流被关闭(如
exit()或return导致流关闭)。
- 输出了一个换行符
示例:
printf("Hello, "); // 不会立即输出,因为没有换行
printf("World\n"); // 输出 "Hello, World",因为遇到换行符
2.2. 全缓冲(Full Buffering)
- 默认情况下,面向文件的文件流(如写入文件的
FILE*)使用全缓冲。 - 缓冲区在以下情况下刷新:
- 缓冲区被填满。
- 主动调用刷新函数(如
fflush(file_stream))。 - 程序正常结束,流被关闭(如
fclose()或exit())。
示例:
FILE *fp = fopen("output.txt", "w");
fprintf(fp, "Buffered output"); // 不会立即写入文件
fflush(fp); // 主动刷新缓冲区,写入文件
fclose(fp); // 关闭文件时自动刷新缓冲区
2.3. 无缓冲(Unbuffered)
- 默认情况下,标准错误流
stderr是无缓冲的(因为需要及时显示错误信息)。 - 缓冲区在每次调用I/O操作时都会刷新,数据直接输出到目标设备。
- 如果通过
setvbuf或setbuf将流设置为无缓冲,则每次调用printf都会立即输出。
示例:
fprintf(stderr, "This is an error message\n"); // 立即输出,不受缓冲机制影响
设置无缓冲流:
setvbuf(stdout, NULL, _IONBF, 0); // 将 stdout 设置为无缓冲
printf("Immediate output"); // 每次调用都会直接输出
2.4. 缓冲区溢出或关闭时刷新
- 如果缓冲区被填满,
stdio会自动刷新。 - 当程序结束或流关闭时(如
fclose()),缓冲区中的内容会被自动刷新。
stdio缓冲机制总结
| 缓冲模式 | 使用场景 | 刷新条件 |
|---|---|---|
| 行缓冲 | stdout面向终端 |
换行符、缓冲区满、调用fflush、流关闭或程序退出 |
| 全缓冲 | stdout面向文件或其他设备 |
缓冲区满、调用fflush、流关闭或程序退出 |
| 无缓冲 | stderr或主动设置无缓冲流 |
每次调用printf或fprintf直接输出 |
缓冲模式可以通过 setvbuf 或 setbuf 自定义,这在调试或控制输出行为时非常有用。
3. 并发场景下的stdio缓冲
在并发场景下,stdio的缓冲机制可能会更令人迷惑一点,不过机制是相通的。
$ cat fork_printf.c
#include <stdio.h>
#include <unistd.h>
int main(void) {
for (int i = 0; i < 2; i++) {
fork();
printf("Hello\n");
}
return 0;
}
$ gcc fork_printf.c
$ ./a.out
Hello
Hello
Hello
Hello
Hello
Hello
$ ./a.out | cat
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
$ # ??? 为什么两次输出内容不一样?是魔法么??!
这就是因为./a.out面向的输出的使用的缓冲方式不同:
- 面向标准输出stdout时,使用行缓冲机制,Hello\n不存放在stdio的缓冲区(内存中),而是直接输出了
- 面向管道输出时, 则使用全缓冲机制,因此第一个Hello\n会存放在缓冲区中,并随着fork一并复制,并再最后程序退出时输出。
也许你觉得我在胡说八道,但是根据计算机中没有魔法的观点,我们一定是有办法验证我们的猜想的。
没错,我们可以使用strace来看到程序的write系统调用,从而验证上述观点。
下一篇将以此为例介绍linux神器之strace的应用场景与使用方式。
stdio.h的缓冲机制解析的更多相关文章
- Printf的缓冲机制
转:https://blog.csdn.net/qq_25424545/article/details/78772959 今天用fork()写程序时候,突然发现自己对Printf的缓冲机制还是有些不够 ...
- 走进C标准库(2)——"stdio.h"中的fopen函数
其他的库文件看起来没有什么实现层面的知识可以探究的,所以,直接来看stdio.h. 1.茶余饭后的杂谈,有趣的历史 在过去的几十年中,独立于设备的输入输出模型得到了飞速的发展,标准C从这个改善的模型中 ...
- GO语言练习:channel 缓冲机制
1.代码 2.运行 3.解析 1.代码 buffer.go package main import ( "fmt" "time" ) func readThre ...
- c语言输入与输出库函数#include<stdio.h>
last modified: 2010-05-28 输入与输出<stdio.h> 头文件<stdio.h>定义了用于输入和输出的函数.类型和宏.最重要的类型是用于声明文件指针的 ...
- C语言中.h和.c文件解析(很精彩)
C语言中.h和.c文件解析(很精彩) 简单的说其实要理解C文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程: 1.预处理阶段 2.词法与语法分析 ...
- C语言中.h和.c文件解析
整理自C语言中.h和.c文件解析(很精彩) Part.1(林锐<高质量C/C++编程>) 通过头文件来调用库功能.在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的 ...
- 走进C标准库(5)——"stdio.h"中的其他部分函数
函数介绍来自:http://ganquan.info/standard-c/ 函数名: freopen 功 能: 替换一个流 用 法: FILE *freopen(char *filename, ...
- 走进C标准库(3)——"stdio.h"中的getc和ungetc
接前文. 再来看看getc和ungetc的实现.在看这两个函数的实现之前,我们先来想一想这两个函数分别需要做的工作. int getc(FILE *stream) 说明:函数getc从stream指向 ...
- java Swing 图片缓冲机制
java Swing 图片缓冲机制: 参考:http://jorneyr.iteye.com/blog/868858#comments package util; import java.awt.ge ...
- 转-C语言中.h和.c文件解析
C语言中.h和.c文件解析(很精彩) 简单的说其实要理解C文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程: 1.预处理阶段 2.词 ...
随机推荐
- IPC-7093A-CN 中文 2020底部端子元器件(BTCs)设计和组装工艺的实施
IPC-7093A 标准为实施底部端子元器件(BTCs)提供了基本的设计和组装指南.具体而言,IPC-7093A 提供了与 BTCs 相关的关键设计.材料.组装.检查.维修.质量和可靠性问题的指南. ...
- 从2s优化到0.1s
前言 分类树查询功能,在各个业务系统中可以说随处可见,特别是在电商系统中. 但就是这样一个简单的分类树查询功能,我们却优化了5次. 到底是怎么回事呢? 背景 我们的网站使用了SpringBoot推荐的 ...
- flask+gunicorn+supervisor部署项目
一.安装模块 pip install gunicorn gevent # 如果使用python supervisor,需要安装模块 pip install supervisor # 建议使用yum安装 ...
- 21stUESTC
数矩形 平面上有 \(n\) 个点,这 \(n\) 个点两两不重合.问这 \(n\) 个点可以组成多少个矩形 请注意:矩形的边不必平行于坐标轴. \(4 ≤ n ≤ 1000\) 保证这些点两两不重合 ...
- ZCMU-1144
简单问题: 就只是如何降低时间的问题罢了:本来这种方法以前学过但是没怎么用所以不太灵活. #include<stdio.h> #define maxn 1000010 int sum[ma ...
- .NET Core 锁(Lock)底层原理浅谈
CPU原子操作 原子操作,指一段逻辑要么全部成功,要么全部失败.概念上类似数据库事物(Transaction). CPU能够保证单条汇编的原子性,但不保证多条汇编的原子性 那么在这种情况下,那么CPU ...
- 获取n级父目录名称
DirectoryInfo GetPrant(DirectoryInfo path, int level) { DirectoryInfo temp = null; if (level > 1) ...
- css var实现网页换肤
前情 最近在做需求开发,要求根据后台传来的配置对网页换肤,按以往的换肤思路应该是写好几套样式做切换达到换肤效果,但是现在想做到能根据后台配置动态修改. 原理 通过css3新增变量特性,把颜色定义为变量 ...
- \r,\n,\r\n的前世今生
前情 最近在逛论坛的时候遇到有人在提问题,为什么\n在苹果手机上不换行,我以前有网上看到过文章,是因为各系统的解析不同,需要使用\r\n来做兼容,自己虽然知道怎么解决,但是不知具体原因,今特来详细了解 ...
- Java线程 interrupt 方法使用异常
背景 需要在异步任务中中断任务的执行,故选择通过调用 interrupt 方法对线程设置中断信号. 在比较耗时的业务代码增加判断 Thread.currentThread().isInterrupte ...