【问题标题】:Stream vs Buffer流与缓冲区
【发布时间】:2021-10-06 00:50:00
【问题描述】:

我是 C 的新手。我目前正在阅读 K&R。在那里,我对其中关于文本流的定义感到困惑“文本流是分成新行的字符序列;每行由 0 个或多个字符组成,后跟一个换行符。” 为了了解这些流,我被介绍了一个新术语,即缓冲区。

我只知道:

  • 输入和输出设备之间的连续数据流(字节或字符)是
    STREAM
  • 主存储器中的临时存储区域,用于临时存储输入或输出数据 是一个BUFFER

我并不是说我是对的,但这是我在这些条件下的基本想法。

我想知道,缓冲区和流实际上是什么,以及这两个东西(即流和缓冲区)如何在 C 实现的非抽象级别中协同工作。

【问题讨论】:

    标签: c io buffer bufferedinputstream


    【解决方案1】:

    定义还不错,其实很好。您可以添加(从面向对象的角度),STREAM 使用 BUFFER。

    可能需要使用 BUFFER,例如性能原因,因为每个系统调用都会带来相对较高的成本。

    与内存访问时间相比,尤其是 IO 系统调用、硬盘或网络访问速度较慢。如果读取或写入仅包含单个字节,它们就会相加。

    【讨论】:

      【解决方案2】:

      两种常见的 I/O 设备抽象是:

      流 - 在设备准备就绪时传输可变数量的字节。

      块 - 传输固定大小的记录。

      缓冲区只是保存正在传输的数据的内存区域。

      【讨论】:

        【解决方案3】:

        您在 C 中有三个流,stdinstdoutstderr,您也可以将使用 fopen 打开的文件视为一个流。 stdin 一般是键盘,stdout 一般是你的显示器,stderr 一般也是你的显示器。但它们不一定是,它们是硬件的抽象。

        例如,如果您没有键盘,而是在银行 ATM 上使用小键盘,那么 stdin 将是小键盘,如果您没有显示器而是有打印机,那么 stdout将是打印机。您可以通过调用您的操作系统来更改他们使用的硬件。同样,您还可以通过调用您的操作系统来改变他们的行为,这超出了您所要求的范围。

        因此,在某种程度上,可以将缓冲区视为与流相关的操作系统分配的内存,用于保存从硬件组件接收到的数据。例如,当您在键盘上键入时,您键入的字符不会被 IDE 直接捕获,它们会从那里移动到缓冲区中,然后您读取缓冲区。

        这就是为什么,例如,您必须在代码开始与您在键盘上输入的任何内容进行交互之前按回车键,因为stdin 是行缓冲的。控制从您的程序传递到操作系统,直到遇到将控制发送回您的程序的东西,在正常情况下,这将是换行符。

        所以在某种程度上,可以这样想,流是设备(键盘、显示器或硬盘驱动器上的文件),缓冲区是在操作系统控制时保存数据的地方,然后在处理数据时与缓冲区进行交互。

        这种抽象允许您以通用方式使用所有这些不同的东西,无论它们是什么,例如:fgets(str, sizeof(str), STREAM) ...流可以是任何输入流,无论是stdin还是文件。

        更进一步,这就是为什么新程序员会被scanf 抛弃,因为int 然后是fgets,因为scanf 从缓冲区中读取int,但将\n 留在缓冲区...然后对fgets 的调用读取\nscanf 留在那里,新程序员想知道为什么他们无法输入任何数据。因此,当您继续学习 C 语言时,您对流和缓冲区的好奇心会为您提供很好的帮助。

        【讨论】:

        • 嘿@LEF,谢谢你的回答让我终于得出结论。这对我帮助很大!
        • 嘿,你能不能给我推荐一本书,帮助我了解 C 的更深层次的实现(即了解它的实际抽象在幕后是如何工作的)。
        • @UdayKiran 我曾经使用过的只是 C 编程语言,在那本小书中有一生的学习时间。但有时它有点太“高级”了,所以深入研究 linux 内核。它是用 C 语言编写的,因为它是开源的,所以您实际上可以查看抽象是如何实现的。例如,当您致电 fgets 时,您可以看到幕后发生的事情。如果您正在寻找一个有趣的项目,使用 Beej 的网络指南编写聊天服务器/客户端设置,它将帮助您开始 linux 编程(并进一步了解流)。
        • @UdayKiran 在服务器/客户端程序方面,深入select函数,你甚至可以使用linux内核的timer_fd功能写一个小游戏什么的,然后在文档方面学习您只需使用 linux 程序员指南(手册页)以及 Internet 上的各种资源。您会看到 stdin 只是作为文件描述符实现的,等等。玩得开心。
        • @UdayKiran 在上面的评论中打错了,编辑的时间已经过期,只是为了清楚它是 timerfd,而不是 timer_fd
        【解决方案4】:

        这些实际上是非常好的工作定义。

        在实际的 C 术语中,缓冲区 是一个数组(通常为 charunsigned char 类型),用于存储数据,无论是作为输入操作的结果,还是在发送之前输出。数组可以声明为固定大小的数组,如

        char buffer[SOME_BUFFER_SIZE];
        

        或动态地,使用

        char *buffer = malloc( SOME_BUFFER_SIZE * sizeof *buffer );
        

        使用动态内存的好处是可以根据需要调整缓冲区的大小;缺点是您必须管理该内存的生命周期。

        对于文本输入/输出,您通常会使用 char 的数组;对于二进制输入/输出,您通常会使用 unsigned char 的数组。

        通过网络进行通信的系统以固定大小的“块”发送数据是相当常见的,因此您可能需要多次读取或写入操作来获取所有数据。想一想 Web 服务器和浏览器 - 服务器以多条消息的形式发送 HTML,浏览器将中间结果存储在缓冲区中。只有在接收到所有数据后,浏览器才会呈现页面:

        Received from Web server       Stored in browser's input buffer
        ------------------------       --------------------------------
        HTTP/1.1 200 OK \r\n           <!DOCTYPE HTML><html
        Content-length: 20\r\n
        <!DOCTYPE HTML><html
        
        HTTP/1.1 200 OK \r\n           <!DOCTYPE HTML><html><head><title>This i
        Content-length: 20\r\n
        ><head><title>This i
        
        HTTP/1.1 200 OK \r\n           <!DOCTYPE HTML><html><head><title>This i
        Content-length: 20\r\n         s a test</title></he
        s a test</title></he
        
        HTTP/1.1 200 OK \r\n           <!DOCTYPE HTML><html><head><title>This i
        Content-length: 20\r\n         s a test</title></head><body><p>Hello, W
        ad><body><p>Hello, W
        
        HTTP/1.1 200 OK \r\n           <!DOCTYPE HTML><html><head><title>This i
        Content-length: 19             s a test</title></head><body><p>Hello, W
        orld!</body></html>            orld!</body></html>
        

        没有正常的服务器以 20 个字符的块发送 HTML,但这应该说明为什么以及如何使用缓冲区。

        【讨论】:

          猜你喜欢
          • 2011-11-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-04
          • 2018-09-10
          • 2019-11-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多