关于标准IO缓冲区的问题

按照标准IO缓冲区可以分为三类:

不缓存类型:

  • 一旦有数据,直接将数据写入到文件

行缓冲类型:

  • 同全缓冲类型
  • 遇到\n时,将数据写入文件

全缓冲类型:

  • 当程序结束,将数据冲洗到文件
  • 当遇见fflush(),将数据冲洗到文件
  • 当文件关闭时,将数据冲洗到文件
  • 当遇到读取操作,将数据冲洗到文件
  • 当改变缓冲区的类型时,将数据冲洗到文件
  • 当缓冲区满了,将数据冲洗到文件

对于标准输出而言,默认是行缓冲

对于标准出错而言,默认是不缓存

对于普通文件而言,默认是全缓冲

#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
// w+ 以读写的方式打开文件,若文件存在则清空文件数据,若文件不存在,则创建文件
FILE *fp = fopen("_a.txt", "w+"); // 将数据写入文件
fputs("123", fp); //暂停程序不让,不让程序正常结束,观察a.txt文件内容
pause();
return 0;
}

我们能看到文件被创建,但是文件内没有内容。

我们来分析一下,我们此时是以普通文件操作,此时是默认的全缓冲,且调用了pause()函数,程序没有正常的结束,所以能猜想到此刻的内容是在缓冲区里,没有被冲洗至文件中。如果我们把pause()进行注释,在执行该程序,我们能看到我们要写入的数据出现在_a.txt文件中。这也是全缓冲类型的第一次情况,当程序结束,将数据读取到文件。

接下来我们来看一下这段代码:

#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
FILE *fp = fopen("_a.txt", "w+"); fputs("123", fp);
fflush(fp); pause();
return 0;
}

印证第二种情况我们直接用冲洗函数fflush(),能看到数据成功读取到文件中。

第三种情况,在fput函数和pause函数中间,调用fclose(),关闭文件。

#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
FILE *fp = fopen("_a.txt", "w+"); fputs("123", fp);
fclose(fp); pause();
return 0;
}

第四种情况,当遇到读取操作,将数据冲洗到文件。

#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
FILE *fp = fopen("_a.txt", "w+"); fputs("123", fp);
fgetc(fp); pause();
return 0;
}

接下来我们看一下通过setvbuf()改变缓冲区的类型,进而对缓冲区的冲洗操作。

简单的介绍一下:setvbuf函数



这图片是我在man手册截图下来的。

int setvbuf(FILE *stream, char *buffer, int mode, size_t size)

stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了一个打开的流。

buffer -- 这是分配给用户的缓冲。如果设置为 NULL,该函数会自动分配一个指定大小的缓冲。

mode -- 这指定了文件缓冲的模式。

_IOFBF 全缓冲
_IOLBF 行缓冲
_IONBF 无缓冲

size --这是缓冲的大小,以字节为单位。

返回值:如果成功,则该函数返回 0,否则返回非零值。

#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
FILE *fp = fopen("_a.txt", "w+");
// setvbuf(fp, NULL, _IONBF, 0);
fputs("123", fp); setvbuf(fp, NULL, _IONBF, 0); pause();
return 0;
}

我们这里的语句是setvbuf(fp, NULL, _IONBF, 0),把普通文件默认的全缓冲,转换成无缓冲,数据在文件中有所显示,所以符合我们的第五条理论。这是为了突出我们的第五条理论,因此将该语句放置在fput语句的后面,当然如果我们在setvbuf(fp, NULL, _IONBF, 0)后面写上相同的fputs语句或者将setvbuf(fp, NULL, _IONBF, 0),放置至fopen语句后面也是会相同的效果。不过第二种情况的理解不同,此刻能成功读取数据不是因为缓冲区类型的转换,而是因为它此刻已经被定义为无缓冲,所以数据直接读取至文件中。

我们这里不妨试一试自己定义一个100字节的buf缓冲区,且在读取数据之前就将缓冲区类型转换至行缓存,观察是否能通过\n来将数据读取至文件当中去。(冲洗行缓冲)。


#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
FILE *fp = fopen("_a.txt", "w+");
char buf[1024]; setvbuf(fp, buf, _IOLBF, 1024); //这里是行缓冲,如果要将数据冲洗至普通文件,注意要加\n
fputs("123\n", fp); pause();
return 0;
}

总之,滞留在缓冲区中的数据有时被称为脏数据(dirty data),脏数据的存在代表程序操作的结果与文件真实状态不一致,若未正常冲洗这些脏数据就退出程序则有可能会造成数据丢失,我们在使用IO对数据操作时要注意对缓冲区的问题

关于标准IO缓冲区的问题的更多相关文章

  1. linux标准IO缓冲(apue)

    为什么需要标准IO缓冲? LINUX用缓冲的地方遍地可见,不管是硬件.内核还是应用程序,内核里有页高速缓冲,内存高速缓冲,硬件更不用说的L1,L2 cache,应用程序更是多的数不清,基本写的好的软件 ...

  2. linux标准io的copy

    ---恢复内容开始--- 1.linux标准io的copy #include<stdio.h> int main(int argc,char **argv) { if(argc<3) ...

  3. 第十三篇:带缓冲的IO( 标准IO库 )

    前言 在之前,学习了 read write 这样的不带缓冲IO函数. 而本文将讲解标准IO库中,带缓冲的IO函数. 为什么要有带缓冲IO函数 标准库提供的带缓冲IO函数是为了减少 read 和 wri ...

  4. C5 标准IO库:APUE 笔记

    C5 :标准IO库 在第三章中,所有IO函数都是围绕文件描述符展开,文件描述符用于后续IO操作.由于文件描述符相关的操作是不带缓冲的IO,需要操作者本人指定缓冲区分配.IO长度等,对设备环境要求一定的 ...

  5. 为什么需要标准IO缓冲?

    (转)标准I/O缓冲:全缓冲.行缓冲.无缓冲 标准I/O库提供缓冲的目的是尽可能地减少使用read和write调用的次数.它也对每个I/O流自动地进行缓冲管理,从而避免了应用程序需要考虑这一点所带来的 ...

  6. 带标准IO带缓存区和非标准IO 遇到fork是的情况分析

    废话不多说 直接代码 #include<stdio.h> #include<sys/types.h> #include<unistd.h> #include< ...

  7. 带缓冲的IO( 标准IO库 )

    前言 在之前,学习了 read write 这样的不带缓冲IO函数.而本文将讲解标准IO库中,带缓冲的IO函数. 为什么要有带缓冲IO函数 标准库提供的带缓冲IO函数是为了减少 read 和 writ ...

  8. 深究标准IO的缓存

    前言 在最近看了APUE的标准IO部分之后感觉对标准IO的缓存太模糊,没有搞明白,APUE中关于缓存的部分一笔带过,没有深究缓存的实现原理,这样一本被吹上天的书为什么不讲透彻呢?今天早上爬起来赶紧找了 ...

  9. 【linux草鞋应用编程系列】_1_ 开篇_系统调用IO接口与标准IO接口

    最近学习linux系统下的应用编程,参考书籍是那本称为神书的<Unix环境高级编程>,个人感觉神书不是写给草鞋看的,而是 写给大神看的,如果没有一定的基础那么看这本书可能会感到有些头重脚轻 ...

随机推荐

  1. controller-tool的简单使用

    介绍 在上一篇code-generator简单介绍中重点介绍了如何使用code-generator来自动生成代码,通过自动生成的代码可以帮助我们像访问k8s内置资源那样来操作我们的CRD,其实就是帮助 ...

  2. React 与 Hooks 如何使用 TypeScript 书写类型?

    React 与 Hooks 如何使用 TypeScript 书写类型? 本文写于 2020 年 9 月 20 日 函数组件与 TS 对于 Hooks 来说是不支持使用 class 组件的. 如何在函数 ...

  3. Java 17 新特性:switch的模式匹配(Preview)

    还记得Java 16中的instanceof增强吗? 通过下面这个例子再回忆一下: Map<String, Object> data = new HashMap<>(); da ...

  4. 好客租房15-jsx中的条件渲染

    jsx中的条件渲染 场景:loding效果 条件渲染:根据条件渲染特定的jsx结构 可以使用if/else或者三元运算符和逻辑和运算符实现 //导入react import React from &q ...

  5. 干掉Switch-Case、If-Else----订阅发布模式+事件驱动模式

    在上位机和下位机或者服务端和客户端通信的时候,很多时候可能为了赶项目进度或者写代码方便直接使用Socket通信,传输string类型的关键字驱动对应的事件,这就有可能导致程序中存在大量的Switch- ...

  6. 学习Linux须知1.0之Linux相关概念、工具(yum、vim)、防火墙等

    温馨提示:重点掌握的前面都标注了 ☆ 一.Linux 是什么? Linux 是一个操作系统. 我们的 Linux 主要是系统调用和内核那两层. 当然直观地看,我们使用的操作系统还包含一些在其上运行的应 ...

  7. 卸载windows安装ubuntu的完全指南

    卸载windows安装ubuntu的完全指南 新配置了一台深度学习服务器,但是预装系统为windows10,与需求不符.于是,自己动手安装ubuntu(18.04).此文为过程记录. 主要步骤: 准备 ...

  8. python求最大公约数和最小公倍数

    1 def gcd(x,y): 2 while(y): 3 t=x%y 4 x=y 5 y=t 6 #print("最小公倍数是:",x*y/x)#最小公倍数是两数之积除以最大公约 ...

  9. c++ 线段树

    关于线段树 线段数是一种区间树 可以看出:叶子即为输入的数 假设一个节点为 x ,则其左儿子为 2x 右儿子为 2x+1 操作解析 约定 变量名 意义 input[] 输入的数 t[] 线段树 其中 ...

  10. 基于web3D展示技术的煤矿巷道3D可视化系统

    地下开采离不开巷道工程.煤矿的生产.运输.排水.通风等各个环节都少不了巷道的支持.在煤矿智能化建设被提上日程的今天,巷道工程的智能化.可视化建设也成了行业趋势.尤其是复杂的井下作业环境,人员信息安全问 ...