C语言中标准输出的缓冲机制

news/2025/3/10 14:32:13/文章来源:https://www.cnblogs.com/AnimeBucket/p/18762631

什么是缓冲区

缓存区是内存空间的一部分,再内存中,内存空间会预留一定的存储空间,这些存储空间是用来缓冲输入和输出的数据,预留的这部分空间就叫做缓冲区

其中缓冲区还会根据对应的是输入设备还是输出设备分为输入缓冲区输出缓冲区

为什么需要缓冲?

直接操作硬件(如屏幕、磁盘)的I/O操作非常耗时。就好比如,你每次调用 printf 都直接向屏幕写入一个字符,会导致频繁的系统调用,极大降低程序效率。 缓冲机制通过将数据暂存在内存中的一块区域(缓冲区),等待满足特定条件时一次性写入目标设备,从而减少系统调用次数。

缓冲的三种模式:

全缓冲(Fully Buffered)

数据会陷入导内存的缓冲区里面,知道缓冲区满了或者我们去手动去刷新我们的缓存区,才会一次性将我们缓存区的内容写入到目标设备。

一般来说,全缓冲一般运用在文件操作的一些场景中,就好比如说我们写入文件的是时候,会使用全缓冲。还有就是重定向输出导文件的时候,stdout会从默认的行缓冲切换为全缓冲给。

 #include <stdio.h>
 int main() {
     FILE *fp = fopen("output.txt", "w");
     fprintf(fp, "Hello, World!");  // 写入缓冲区,但不会立即写入文件
     // 此时文件内容为空
     fflush(fp);                    // 手动刷新缓冲区,内容写入文件
     fclose(fp);                    // 关闭文件时也会自动刷新
     return 0;
 }

注意我们代码中的这两个函数,fflush()fclose()如果没有这两个函数的话,我们写入的内容可能会丢失。

行缓冲(Line Buffered)

在这种情况下,当在输入和输出中遇到换行符或者缓冲区满的时候,执行真正的I/O操作

这种缓冲模式一般运用再终端输出和交互式程序中,就好比如需要及时的显示提示信息的场景(就好像自动贩卖机提示你printf(“please input your money:”))。

行缓冲一般再遇到换行符\n,缓冲区满的时候,程序正常退出的时候还有手动刷新时后触发。

 #include <stdio.h>
 #include <unistd.h>  // 用于 sleep 函数
 
 int main() {
     printf("Start...");   // 无换行符,不刷新
     sleep(2);             // 等待2秒
     printf("End\n");      // 遇到换行符,立即刷新,输出 "Start...End"
     return 0;
 }

结果:程序会等待2秒后一次性输出 Start...End

无缓冲(Unbuffered)

数据直接写入目标设备,不经过缓冲区。每次 I/O 操作都会立即生效。其实我们看名字应该就能想到时没有缓冲,那就是说,没有任何触发条件,写入操作集合生效。

一般用在标准错误流stderr和需要实时反馈的场景,好比如调试信息,关键日志。

                           #include <stdio.h>
 
 int main() {
     fprintf(stderr, "Error: File not found!\n");  // 立即输出
     printf("This is stdout message.");            // 可能延迟输出(行缓冲)
     return 0;
 }

结果stderr 的输出会立即显示,而 stdout 的输出可能延迟。

如何控制缓冲模式?

我们知道了有那些缓冲模式之后,我们是不是可以去想想有哪些控制缓冲模式的方法。

我们可以使用setvbuf函数来自定义缓冲行为。

有这几个参数:

  1. _IOFBF:全缓冲

  2. _IOLBF:行缓冲

  3. _IONBF:无缓冲

常见的问题与解决方案:

问题一:输出顺序不符合预期

就好比如这个代码:

 #include "stdio.h"
 int main() {
     printf("A");
     fprintf(stderr, "B");  // stderr无缓冲
     printf("C");
 }

我们可以看到他的输出结果是这样的:

BAC

这种情况就是输出顺序不符合我们的预期,我们可以使用fflush来去刷新我们的缓存区。

 #include "stdio.h"
 int main() {
     printf("A");
     fflush(stdout);
     fprintf(stderr, "B");  // stderr无缓冲
     printf("C");
 }

这样写就可以解决这个问题了。

问题二:调试信息丢失

 #include "stdio.h"
 int main() {
     printf("Debug info"); // 无换行符
     int *p = NULL;
     *p = 42; // 段错误,程序崩溃
 }

首先我们来分析一下这个代码,我们的printf中没有\n所以会造成Debug info可能不会立即显示在控制台上,所以我们首先是得去使用fflush来刷新缓冲区。

然后我们定义了一个空指针,然后我们又尝试尝试通过 p 访问内存并给它赋值,但是这样肯定是有问题的,由于 pNULL,这将导致段错误(Segmentation Fault),程序会崩溃。

这种情况我们也是可以使用fflush来进行刷新缓冲区的。

问题三:输入输出混合时的提示延迟

这个是这样的:

image-20250310141818702

这里的我们只能先输入,然后才会提示我们的输出。

我们还是直接使用fflush去刷新缓冲区就可以了。

总结

C语言的缓冲机制是I/O高效性的核心设计,但需要开发者深刻理解其行为。

  • 缓冲模式决定刷新时机:全缓冲看容量,行缓冲看换行,无缓冲即写即走。

  • 手动控制是王道:在需要实时性的地方,用fflushstderr

  • 跨平台注意细节:不同系统对行缓冲的实现可能有差异。

  •  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/896817.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k8s回调函数-cnblog

回调函数 Kubernetes 为容器提供了生命周期回调。 回调使容器能够了解其管理生命周期中的事件,并在执行相应的生命周期回调时运行在处理程序中实现的代码。Kubernetes 支持 PostStart 和 PreStop 事件。 当一个容器启动后,Kubernetes 将立即发送 PostStart 事件;在容器被终结…

Transfomer 中的强制教学(Teacher Forcing)

在预测阶段, 我们希望输入 "天雷滚滚我好怕怕" 和 "[cls]" 能预测出下一个token: 劈 于是我们在训练阶段,我们的输入是 "天雷滚滚我好怕怕" 和 “[cls]劈得我浑身掉渣渣”, 由于我们希望"[cls]" 能预测出 “劈” 字。 我们在损失函…

jenkins安装后可用插件版本需要高版本的jenkins才能使用

首先我们直接用清华镜像源https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/,比如我们要找Jenkins2.429对应版本 emmmm,没有429,427凑合用吧 获取这个地址,放到updatesite 进服务器,找到default.json,替换掉它,然后重启jenkins。 cp default.json default.json.ba…

​党政机关数字化转型必备:10款信创认证项目管理工具深度测评

党政机关数字化转型是适应时代发展、提升治理效能的关键举措。在这一过程中,信创认证项目管理工具发挥着至关重要的作用。它们不仅能助力项目高效推进,还能保障信息安全与合规性。以下将对10款信创认证项目管理工具进行深度测评,为党政机关在数字化转型道路上的工具选择提供…

材料焊接工艺大全-芯晨微纳(河南) -专注于激光代加工

一、材料焊接中的“焊接力” 材料焊接,准确说应该为“材料互联”,是通过物理力、化学力、机械作用力等方式将原本相互隔离的材料连为一体的过程。 物理力中的四种基本力是万有引力、电磁力、强相互作用力和弱相互作用力。具体的,按性质(根据它们的物理性质来命名和分类)分…

Zabbix 7.2 + Grafana 中文全自动安装ISO镜像

简介 ​基于Zabbix 官方的Alma Linux 8 作为基础镜像。镜像源都改为国内大学镜像站,自动联网安装Zabbix+Grafana。安装中文字体、Zabbix和Grafana也配置默认中文。Zabbix 也指定中文字体,绘图无乱码。配置时区为东八区,Zabbix配置Web时区也为东八区。Grafana自动安装zabbix源…

第十八章 项目绩效域(2025年详细解析版)

目录导学太极八卦与项目管理体系项目管理整体框架图绩效域原则和绩效域的关系18.1 干系人绩效域干系人绩效域概述定义预期目标绩效要点 :促进干系人参与目的作用促进干系人参与要做的六项活动与其他绩效域的相互作用(了解)关联为什么要促进干系人参与?为什么干系人重要?执…

PCA9306DCTR特征—400kHz I2C/SMBus电压电平转换器/S9S08DZ60F2VLCR/CC2650F128RSMR/MAX4715EXK SPST 开关

PCA9306DCTR是一款2 位双向 400kHz I2C/SMBus 电压电平转换器,可以在 1.2V 到 5V 之间实现双向电压转换而无须使用方向引脚。PCA9306DCTR是一款2 位双向 400kHz I2C/SMBus 电压电平转换器,可以在 1.2V 到 5V 之间实现双向电压转换而无须使用方向引脚。PCA9306DCTR具有低导通状…

快速理解Kubernetes 系统架构

Kubernetes 架构解析1. 整体架构:管理层 + 执行层 管理层(Master 节点)——"老板团队"API 服务器(kube-apiserver) ▶️ 公司的"前台",所有指令必须通过这里传达(如部署应用、查看状态) 调度器(kube-scheduler) ▶️ 像"项目经理",决…

001TypeScript开发实战

1、点击文件,点击打开文件夹 2、选择文件夹,打开文件夹 3、点击终端,新建终端 4、在终端内输入npm init vue@latest 按回车 5、写入项目名称 vue3-ts-cms 6、选择:这里我们先这样选择,Router(单页面应用开发)Pinia(状态管理)这次我们搭建一下7、 这里我们选择NO 可以看见…

​信创概念深度科普:从3大政策背景到5类典型应用场景全解析

信创,即信息技术应用创新产业,近年来在我国的科技发展领域占据着愈发重要的地位。它不仅仅是一个简单的产业概念,更是我国实现科技自立自强、保障国家信息安全的关键支撑。随着数字化时代的加速推进,信创产业迎来了前所未有的发展机遇,其涵盖的范围广泛,涉及到众多的政策…

数字逻辑 可编程阵列逻辑(PAL)

数字逻辑 可编程阵列逻辑(PAL) 参考书籍:数字逻辑基础与Verilog设计 原书第3版 P402 这里先给个可编程逻辑阵列(PLA)的例子。PLA的常用示意图中画X的就是选中的。 所以图 B.27 的计算过程为: \[\begin{align} P_1=& \ x_1x_2\\[1mm] P_2=& \ x_1 \overline x_{3…