Linux c学习--从标准输入输出看流和缓冲区

学习标准输入输出,我们都会遇到一个概念,流和缓冲区,但到底什么是流,什么是缓冲区呢?

      书《C Primer Plus》上说,C程序处理一个流而不是直接处理文件。后面的解释十分抽象:流(stream)是一个理想化的数据流,实际输入或输出映射到这个数据流。这个流具体是一个怎么样的东西呢?

流这个定义非常的形象。我们可以这样理解:
     你声明一个FILE *fp ,并把fopen(某个文件)返回的值赋予fp这两个动作就相当于建立了一个水龙头,当你用getc(fp)之类的输入函数读取文件字符时就相当于拧开了水龙头,每读取一个字符,这个文件就像水一样的流动一下,fp所指的地址自然就向后移动了一位。
[cpp] view plaincopy
  1. int ch;  
  2. while((ch=getc(fp))!=EOF)  
  3.     putchar(ch);  

       你看这个循环,可以读取一个文件的所有字符。如果不是流的话,ch永远是第一个字符,不会更新。也可以理解为,fp自动++(一个字符的大小)。
    
但流的概念意味着什么呢?
--流是独立于设备之外而操纵外设一种逻辑手段。 
--大多数外设都是互异的,所以(操纵)它们需要专门的编程技术。 
--流对程序员隐藏这些不同点,而准许他们以同样的方式来处理大多数外设。 
--考虑到一连串的字符需要一次读一个,流(相当于)是具有缓冲作用的接口。 
--个人计算机都是基于流架构的。

各大权威对流的说法有些不一致,我认为流既是数据的源或目的地的抽象,也是源和目的地之间流动信息的表示。但流起码都暗含以下的几个方面: 
1、流是一个抽象的概念,是对信息的一种表达;在程序中,流就是对某个对象输入输出信息的抽象。就像运输工具是对一切运动载体的抽象一样。 
2、流是一种“动”的概念,静止存储在介质上的信息只有当它按一定的序列准备“运动”时才称为流。“从程序移进或移出字节”就是“动”的表现。静止的信息具有流的潜力,但不一定是流,就像没有汽油不能行走的汽车一样,它具有运输工具的潜力,但它还不是运输工具(因为它很有可能被当作房子来用了,我就在大街上看见有精明的商人用火车车厢来做酒吧)。
3、流有源头也有目的地;程序中各种移动的信息都有其源和目的,记得编程(特别是汇编)时,老是要确定好某个操作的源操作数和目的操作数。借用佛教一言也即是:“万物皆有因果”,这也就像长江一样,西自唐古拉,而东去太平洋。在高速公路上飞跑的汽车,它必有其出发地和目的地。
4、流一定带有某种信息,没有任何内容的流带着自身来表达“空”信息。就像运输工具一样,它不运货的时候就运着自己这一身的零件(包括驾驶员)并把一样东西运到目的地,那就是它自己和一个“跑空车”的信息。流有最小的信息单元就是二进制位,含有最小的信息包就是字节,C标准库提供两种类型的流:二进制流(binary stream)和文本流(text stream)。二进制流是有未经处理的字节构成的序列;文本流是由文本行组成的序列。而在著名的UNIX系统中,文本流和二进制流是相同的(identical)。
5、流有源头也有目的地,那么它必定与源头和目的地相关联。但人们操作流的时候,最关心的还是其目的地,也就是一个定向(orientation)的意思,就像司机运货一样,它首要关心的问题是目的地,而非起点(操作者都知道)。在C语言中,通过打开流来关联流及其目的地,使用的函数是fopen(),该函数返回一个指向文件的指针(FILE *),该指针包含了足够的可以控制流准确地到达目的地的信息。
FILE是一个结构体(摘自TC2.0中stdio.h文件)
 
[cpp] view plaincopy
  1. /* Definition of the control structure for streams 
  2. */  
  3. typedef struct  {  
  4.         short           level;          /* fill/empty level of buffer */  
  5.         unsigned        flags;          /* File status flags    */  
  6.         char            fd;             /* File descriptor      */  
  7.         unsigned char   hold;           /* Ungetc char if no buffer */  
  8.         short           bsize;          /* Buffer size          */  
  9.         unsigned char   *buffer;        /* Data transfer buffer */  
  10.         unsigned char   *curp;          /* Current active pointer */  
  11.         unsigned        istemp;         /* Temporary file indicator */  
  12.         short           token;          /* Used for validity checking */  
  13. }       FILE;                           /* This is the FILE object */  

     将它称为流控制结构体(control structure for streams)真好表现出其功能来。举个例子就好像一卡车司机要把货物运到X公司,公司主管就会给他一张地图及X公司的基本信息,这些材料所提供的信息如果足够的话,那么它就能指导着司机准确地将货物送达了。C中FILE这个结构体所起的作用就好像是运输公司把一切有用的指导信息封装起来的档案袋一样。而已有关联的流要终止这种关联,就必须关闭流,使用的函数是fclose(),就像运货公司若不再给X公司运货了,那么他们就必须要终止合作协议了。
    这里要注意的是:C语言中stdin、stdout、stderr分别是标准输入流、标准输出流及标准出错流的逻辑目的,他们都默认对应相应的物理终端。在程序运行伊始,不需要进行open()操作,流自动打开。

 
那缓冲区又是什么意思呢?
缓冲区(Buffer): 
    为了匹配计算机快速设备和慢速设备间的通信步伐,计算机中大量使用硬件缓冲区(如CPU中的Cache,内存相对于硬盘和CPU),流是传输信息的一种逻辑表示,对流的各种不同操作也可能存在使用缓冲的需求。但是这里的buffer只是一种逻辑概念,不是物理设备。缓冲区存在于流与具体的设备终端或者存储介质上的文件之间。就好像运货到一个公司里一样,合同上的要求是运到X公司,但是实际上是真的把货物运到X公司的总部大楼吗?不是。应该是运到X公司的仓库中。这里的仓库就有点像我们所说的缓冲区了。也可以这么说,流运动到目的,先经过的是缓存区。
 
以scanf() printf()为例:                     
•   缓冲区(流)负责在输入/输出设备和程序之间建立联系。
–输入设备->内存缓冲区(stdin)->程序
–程序->内存缓冲区(stdout)->输出设备
•   是一块临时的存储区域,或在内存中,或在设备的控制卡上
 
 
. 缓冲类型。

标准库提供缓冲是为了减少对read和write的调用。提供的缓冲有三种类型(整理自APUE):

  • 全缓冲。

在这种情况下,实际的I/O操作只有在缓冲区被填满了之后才会进行。对驻留在磁盘上的文件的操作一般是有标准I/O库提供全缓冲。缓冲区一般是在第一次对流进行I/O操作时,由标准I/O函数调用malloc函数分配得到的。

术语flush描述了标准I/O缓冲的写操作。缓冲区可以由标准I/O函数自动flush(例如缓冲区满的时候);或者我们对流调用fflush函数。

  • 行缓冲

在这种情况下,只有在输入/输出中遇到换行符的时候,才会执行实际的I/O操作。这允许我们一次写一个字符,但是只有在写完一行之后才做I/O操作。一般的,涉及到终端的流--例如标注输入(stdin)和标准输出(stdout)--是行缓冲的。

  • 无缓冲

标准I/O库不缓存字符。需要注意的是,标准库不缓存并不意味着操作系统或者设备驱动不缓存。

当然,我们常用的scanf()  与  printf() 属于行缓冲,下面我们来看个例子,可以帮助我们理解缓冲区在标准输入输出中的作用:
[cpp] view plaincopy
  1. <pre class="cpp" name="code">#include <stdio.h>  
  2.   
  3. int main()  
  4. while(1);  
  5. {  
  6.     printf("hello world");  
  7.     while(1);  
  8. }  
 
我们看看输出结果:
[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/char1$ gcc -o 1 1.c  
  2. fs@ubuntu:~/qiang/char1$ ./1  
打出是个空的,为什么呢?

我们上面提到标准输入输出是行缓冲,即一行满了才会刷新,那什么是刷新呢?刷新就是将数据从缓冲区取出来,真正能刷新,要满足什么条件呢?

1、满刷新,即一行满了(1024个字节)才会刷新;

2、遇到'\n'会刷新;

3、调用fflush()函数;

4、程序结束 fclose();

我们可以看到上面的程序,应为有while(1),程序一直没有结束,没有'\n',没有满行,没有fflush(),所以并不会输出;

这样理解的话,我们可以改动一下了,就写一个吧,加'\n':

[cpp] view plaincopy
  1. #include <stdio.h>  
  2.   
  3. int main()  
  4. {  
  5.     printf("helloworld\n");  
  6.     while(1);  
  7. }  

执行结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/char1$ gcc -o 1 1.c  
  2. fs@ubuntu:~/qiang/char1$ ./1  
  3. helloworld  



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

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

相关文章

ubuntu vim8.0源码安装

安装篇 从https://github.com/vim/vim下载相应zip源码文件&#xff0c;利用unzip vim-master.zip 命令解压到当前用户目录&#xff0c;即~&#xff1b; 解压后进入vim的src目录&#xff0c;首先&#xff0c;即运行 sudo apt-get updata &#xff08;更新系统软件源&#xff09;…

Linux C学习---递归函数

最近学习到了递归&#xff0c;刚开始看&#xff0c;真是头大&#xff0c;函数里面嵌套其本身&#xff0c;到底是怎么个流程啊&#xff1f; 现在&#xff0c;咱们先了解下递归函数的数学原理&#xff1a; 高中的时候就出现很多递归函数&#xff0c;应该是在“级数”那里的习题中…

Java判断两个Date是不是同一天

From: https://blog.csdn.net/xingchenbingbuyu/article/details/82734695 Java判断两个Date是不是同一天 1. 利用Calendar Calendar cal1 Calendar.getInstance(); Calendar cal2 Calendar.getInstance(); cal1.setTime(date1); cal2.setTime(date2); boolean sameDay ca…

js二级下拉被flash档住的解决办法

在<object></object>及以内的代码加入到<script>标签对内<script language"javascript" type"text/javascript"> <param name"wmode" value"transparent" />//背景透明 </script> 转载于:https:/…

iOS:以前笔记,未整理版。太多了,先放着吧。。。。。。。

1、**************************************************************** 单例共享 **************************************************************** 单例 共享信息 .m static OneT *newone nil; (instancetype)shalldata { if (newone nil) { newone [[OneT alloc]init]…

C语言经典编程题--哥德巴赫猜想 、完数 、猜数字等

一、 验证歌德巴赫猜想&#xff1a;任意一个不小于6的偶数都可以表示成两个素数的和。从键盘任意给一个符合条件的数&#xff0c;输出相应的两个素数。 素数&#xff1a;指在一个大于1的自然数中&#xff0c;除了1和此整数自身外&#xff0c;没法被其他自然数整除的数 代码如下…

Calendar的DAY_OF_MONTH, DAY_OF_YEAR, DATE的区别

From: https://blog.csdn.net/weixin_34233679/article/details/87286628 cal1.add(Calendar.DAY_OF_MONTH,1); cal1.add(Calendar.DAY_OF_YEAR,1); cal1.add(Calendar.DATE,1); 就单纯的add操作结果都一样&#xff0c;因为都是将日期1 就没有区别说是在月的日期中加1还是…

Cisco网络防火墙配置方法

这篇文章主要介绍了Cisco网络防火墙配置方法,需要的朋友可以参考下  由于网络防火墙默认禁止所有的通信&#xff0c;因为&#xff0c;只有对其进行适当配置后&#xff0c;才能实现正常的网络通信。  如何配置Cisco网络防火墙  1.进入全局配置模式  ciscoasa#configure …

linux远程登录三种方式telnet,ssh,vnc

linux远程连接三种方式telnet&#xff0c;ssh&#xff0c;vnctelnet和ssh服务只能实现基于字符界面的远程控制&#xff0c;如果要基于图形界面进行远程控制&#xff0c;可以借助免费的VNC来完成。一、telnet连接1.首先进入终端&#xff0c;查看是否安装了telnet服务。linux默认…

老司机学习MyBatis之如何通过select返回Map

From: https://blog.csdn.net/Gaomb_1990/article/details/80638177 一、案例 当要查询的结果是一个Map的时候&#xff0c;这里分为两种情况&#xff1a; ①返回单条记录 <select id"getUserByIdReturnMap" resultType"map"> select id, log…

大数据之Yarn——Capacity调度器概念以及配置

试想一下&#xff0c;你现在所在的公司有一个hadoop的集群。但是A项目组经常做一些定时的BI报表&#xff0c;B项目组则经常使用一些软件做一些临时需求。那么他们肯定会遇到同时提交任务的场景&#xff0c;这个时候到底如何分配资源满足这两个任务呢&#xff1f;是先执行A的任务…

C#基于Socket的简单聊天室实践

序&#xff1a;实现一个基于Socket的简易的聊天室&#xff0c;实现的思路如下&#xff1a; 程序的结构&#xff1a;多个客户端一个服务端&#xff0c;客户端都是向服务端发送消息&#xff0c;然后服务端转发给所有的客户端&#xff0c;这样形成一个简单的聊天室功能。 实现的细…

C/C++经典面试题

面试题1&#xff1a;变量的声明和定义有什么区别 为变量分配地址和存储空间的称为定义&#xff0c;不分配地址的称为声明。一个变量可以在多个地方声明&#xff0c;但只能在一个地方定义。加入extern修饰的是变量的声明&#xff0c;说明此变量将在文件以外或在文件后面部分定义…

Java跳出多重循环

From: https://www.cnblogs.com/fastfn/p/9777067.html 场景&#xff1a;很多的时候需要做到跳出多重循环&#xff0c;而在Java中虽然后goto关键字&#xff0c;但是是保留字&#xff0c;并没有启用。而在处理分支结构的if...else,switch...case,好像都达不到想要的效果。 作为…

CCF 节日

问题描述有一类节日的日期并不是固定的&#xff0c;而是以“a月的第b个星期c”的形式定下来的&#xff0c;比如说母亲节就定为每年的五月的第二个星期日。现在&#xff0c;给你a&#xff0c;b&#xff0c;c和y1, y2(1850 ≤ y1, y2 ≤ 2050)&#xff0c;希望你输出从公元y1年到…

回文数算法

1、回文数&#xff1a;一种数字&#xff0c;如&#xff1a;12321, 这个数字正读是12321&#xff0c;倒读也是12321&#xff0c;即&#xff1a;将这个数的数字按相反的顺序重新排列后&#xff0c;所得到的数和原来的数一样。回文数判别算法&#xff08;java实现&#xff09;impo…

深入了解scanf() getchar()和gets()等函数之间的区别

----------------------------------------------------| 问题描述一&#xff1a;&#xff08;分析scanf()和getchar()读取字符&#xff09; |-------------------------------------------------- scanf(), getchar()等都是标准输入函数&#xff0c;一般人都会觉得这几个函数…

java基础集合简介Map(三)下

From: https://www.cnblogs.com/douyu2580860/p/8358768.html --Map接口简介 今天来看一看map集合&#xff0c;map映射接口&#xff0c;用于存放键值对&#xff0c;<key,value>&#xff0c;通过key来查找value,顾名思义key不能为空&#xff0c;唯一且不重复&#xff0c;不…

横向量与矩阵的乘积

设Tj(Tj1, Tj2, ..., Tjn)为横向量。而iδ是Tj中第δ位不为零的元素&#xff0c;1≤δ≤z。 令hjTjH, 则hj是TH的第j行。且有 hjΣ(ki1, i2, ..., iz)Tj,kHk. 从上式看&#xff0c; &#xff08;1&#xff09;可以将横向量的第k位视为右边矩阵第k行是否叠加的控制信号。 &#…

MySQL日期时间函数大全(转)

DAYOFWEEK(date) 返回日期date是星期几(1星期天,2星期一,……7星期六,ODBC标准)mysql> select DAYOFWEEK(1998-02-03);   -> 3 WEEKDAY(date)  返回日期date是星期几(0星期一,1星期二,……6 星期天)。 mysql> select WEEKDAY(1997-10-04 22:23:00);   -> 5 my…