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是终端)的文件流使用行缓冲
  • 缓冲区在以下情况下刷新
    1. 输出了一个换行符 \n
    2. 缓冲区被填满
    3. 主动调用刷新函数(如 fflush(stdout))。
    4. 程序正常结束,流被关闭(如 exit()return 导致流关闭)。

示例

printf("Hello, ");    // 不会立即输出,因为没有换行
printf("World\n"); // 输出 "Hello, World",因为遇到换行符

2.2. 全缓冲(Full Buffering)

  • 默认情况下,面向文件的文件流(如写入文件的FILE*使用全缓冲
  • 缓冲区在以下情况下刷新
    1. 缓冲区被填满
    2. 主动调用刷新函数(如 fflush(file_stream))。
    3. 程序正常结束,流被关闭(如 fclose()exit())。

示例

FILE *fp = fopen("output.txt", "w");
fprintf(fp, "Buffered output"); // 不会立即写入文件
fflush(fp); // 主动刷新缓冲区,写入文件
fclose(fp); // 关闭文件时自动刷新缓冲区

2.3. 无缓冲(Unbuffered)

  • 默认情况下,标准错误流stderr无缓冲的(因为需要及时显示错误信息)。
  • 缓冲区在每次调用I/O操作时都会刷新,数据直接输出到目标设备。
  • 如果通过 setvbufsetbuf 将流设置为无缓冲,则每次调用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或主动设置无缓冲流 每次调用printffprintf直接输出

缓冲模式可以通过 setvbufsetbuf 自定义,这在调试或控制输出行为时非常有用。

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的缓冲机制解析的更多相关文章

  1. Printf的缓冲机制

    转:https://blog.csdn.net/qq_25424545/article/details/78772959 今天用fork()写程序时候,突然发现自己对Printf的缓冲机制还是有些不够 ...

  2. 走进C标准库(2)——"stdio.h"中的fopen函数

    其他的库文件看起来没有什么实现层面的知识可以探究的,所以,直接来看stdio.h. 1.茶余饭后的杂谈,有趣的历史 在过去的几十年中,独立于设备的输入输出模型得到了飞速的发展,标准C从这个改善的模型中 ...

  3. GO语言练习:channel 缓冲机制

    1.代码 2.运行 3.解析 1.代码 buffer.go package main import ( "fmt" "time" ) func readThre ...

  4. c语言输入与输出库函数#include<stdio.h>

    last modified: 2010-05-28 输入与输出<stdio.h> 头文件<stdio.h>定义了用于输入和输出的函数.类型和宏.最重要的类型是用于声明文件指针的 ...

  5. C语言中.h和.c文件解析(很精彩)

    C语言中.h和.c文件解析(很精彩)   简单的说其实要理解C文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程: 1.预处理阶段 2.词法与语法分析 ...

  6. C语言中.h和.c文件解析

    整理自C语言中.h和.c文件解析(很精彩) Part.1(林锐<高质量C/C++编程>) 通过头文件来调用库功能.在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的 ...

  7. 走进C标准库(5)——"stdio.h"中的其他部分函数

    函数介绍来自:http://ganquan.info/standard-c/ 函数名: freopen 功  能: 替换一个流 用  法: FILE *freopen(char *filename, ...

  8. 走进C标准库(3)——"stdio.h"中的getc和ungetc

    接前文. 再来看看getc和ungetc的实现.在看这两个函数的实现之前,我们先来想一想这两个函数分别需要做的工作. int getc(FILE *stream) 说明:函数getc从stream指向 ...

  9. java Swing 图片缓冲机制

    java Swing 图片缓冲机制: 参考:http://jorneyr.iteye.com/blog/868858#comments package util; import java.awt.ge ...

  10. 转-C语言中.h和.c文件解析

    C语言中.h和.c文件解析(很精彩)   简单的说其实要理解C文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程:       1.预处理阶段 2.词 ...

随机推荐

  1. C# 开发的数据采集及云端监控系统

    前言 推荐C#语言开发的堤坝渗透地质数据采集及云端监控系统.希望本文能够为大家提供有价值的信息和参考. 项目介绍 使用数十个 .NET 客户端控制硬件设备进行工作,采集数据并进行处理. 管理人员通过 ...

  2. 接口文档解决方案之Torna

    ◆一.开源项目简介 接口文档解决方案,目标是让接口文档管理变得更加方便.快捷.Torna采用团队协作的方式管理和维护接口文档,将不同形式的文档纳入进来统一维护. Torna弥补了传统文档生成工具(如s ...

  3. (系列十三)Vue3+Echarts搭建超好看的系统面板

    说明 该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发). 该系统文章,我会尽量说的非常详细,做到不管新手.老手都能看懂. 说明:OverallAuth2 ...

  4. ZCMU-1133

    emm就直接看的前辈的了. 唉 #include <stdio.h> #include <string.h> #include <algorithm> //我不成熟 ...

  5. 如何给自己的网站添加live2D女仆

    <script type="text/javascript" charset="utf-8"  src="https://files.cnblo ...

  6. electron项目build报资源包下载出错

    前情 公司有个桌面端项目是基于Electron开发的. 坑 上一次遇到npm install时安装electron无法安装,通过配置.npmrc文件解决了,今天在在执行npm run build的时候 ...

  7. 【Amadeus原创】Docker安装Nginx,并配置端口转发,配置SSL

    1,docker安装Nginx [root@hecs-29489 ~]# docker pull nginx Using default tag: latest latest: Pulling fro ...

  8. 技术漫谈|IVR通用开发框架简说

    IVR为Interactive Voice Response的缩写,意为交互式语音应答(系统).它可以应答客户的呼叫,然后为呼叫者提供语音导航或自助服务,呼叫者可通过按键输入或使用语音命令进行选择.随 ...

  9. vmagent如何快速采集和转发Metrics

    vmagent如何快速采集和转发Metrics 本文介绍了vmagent的设计细节,参考自:vmagent-how-it-works VictoriaMetrics agent是一个轻量级工具,用于采 ...

  10. Qt/C++开发经验小技巧306-310

    远程过很多人电脑远程分析问题,发现一个通病,比如明明修改了某个数据库或者配置文件,结果运行程序一看,还是旧的数据,死活都不行,查了半天程序,始终找不到问题所在.最终查到原来是程序读取的文件根本不是修改 ...