了解C语言中的缓冲

时间:2015-01-16 22:44:03

标签: c windows gcc

我很难理解缓冲的深度,尤其是在C编程中,我已经在这个主题上搜索了很长时间,但到目前为止还没有找到令人满意的东西。

我会更具体一点: 我确实理解它背后的概念(即通过不同的硬件设备协调操作并最小化这些设备的速度差异)但我希望对这些以及其他可能的缓冲原因进行更全面的解释(并且我完全是指越长越好越好。给出一些具体的例子,说明如何在I / O流中实现缓冲。

其他问题是我注意到缓冲区刷新中的一些规则并不是我的程序,因为这听起来像下面这个简单的片段:

#include <stdio.h>

int main(void)
{
    FILE * fp = fopen("hallo.txt", "w");

    fputc('A', fp);
    getchar();
    fputc('A', fp);
    getchar();

    return 0;
}

该程序旨在证明当第一个getchar()被调用时,即将发生的输入将立即刷新任意流,但这并不像我尝试的那样经常发生并且我想要的修改很多 - 它根本不会发生stdout(例如printf()),流被刷新而没有请求任何输入也会否定规则因此我错误地理解了这个规则或者还有其他需要考虑的因素

我在Windows 8.1上使用Gnu GCC。

更新

我忘了问我在某些网站上看过人们如何参考例如字符串文字作为缓冲区甚至数组作为缓冲区;这是正确的还是我错过了什么? 请解释这一点。

3 个答案:

答案 0 :(得分:6)

buffer 这个词用于计算机科学中的许多不同的事情。在更一般的意义上,它是临时存储数据的任何内存,直到它被处理或复制到最终目的地(或其他缓冲区)。

正如您在问题中暗示的那样,有许多类型的缓冲区,但作为一个广泛的分组:

  1. 硬件缓冲区:这些缓冲区是在移动到HW设备之前存储数据的缓冲区。或者在从HW设备接收数据时存储数据的缓冲区,直到应用程序处理它为止。这是必需的,因为I / O操作通常具有存储器和时序要求,并且缓冲器满足这些要求。想想直接读/写内存的DMA设备,如果内存设置不正确,系统可能会崩溃。或声音设备必须具有亚微秒的精度,否则它将无法正常工作。

  2. 缓存缓冲区:这些缓冲区是在写入/读取文件/设备之前对数据进行分组的,这样可以提高性能。

  3. 辅助缓冲区:您可以将数据移入/移出此类缓冲区,因为它更适合您的算法。

  4. 案例#2是您的FILE*示例。想象一下,对 write 系统调用(Win32中的WriteFile())的调用只需要1ms即可调用每个字节加1us(对我来说,事情在现实世界中更复杂)。然后,如果你这样做:

    FILE *f = fopen("file.txt", "w");
    for (int i=0; i < 1000000; ++i)
        fputc('x', f);
    fclose(f);
    

    如果没有缓冲,此代码将占用1000000 * (1ms + 1us),大约需要1000秒。但是,使用10000字节的缓冲区,将只有100个系统调用,每个10000字节。那将是100 * (1ms + 10000us)。那只是0.1秒!

    另请注意,操作系统将自行执行缓冲操作,以便使用最有效的大小将数据写入实际设备。那将同时是一个硬件和缓存缓冲区!

    关于刷新问题,文件通常只在关闭或手动刷新时刷新。某些文件(例如stdout)是行刷新的,也就是说,只要写入'\n'就会刷新它们。此外stdin/stdout也很特别:当您从stdin阅读时,stdout会被刷新。其他文件不受影响,仅为stdout。如果您正在编写交互式程序,这很方便。

    我的情况#3就是你做的时候:

    FILE *f = open("x.txt", "r");
    char buffer[1000];
    fgets(buffer, sizeof(buffer), f);
    int n;
    sscanf(buffer, "%d", &n);
    

    使用缓冲区保存文件中的一行,然后解析该行中的数据。是的,您可以直接调用fscanf(),但在其他API中可能没有相同的功能,而且您可以通过这种方式获得更多控制:您可以分析类型,如行,跳过注释,计算行...... / p>

    或者想象一次只能收到一个字节,例如键盘。您只需在缓冲区中累积字符,并在按下Enter键时解析该行。这就是大多数交互式控制台程序所做的事情。

答案 1 :(得分:1)

第一个问题有点过于宽泛。在许多情况下使用缓冲,包括实际使用前的消息存储,DMA使用,加速使用等。简而言之,整个缓冲的事情可以概括为&#34;保存我的数据,让我在你对数据执行某些操作时继续执行&#34;。

有时您可能会在将缓冲区传递给函数后修改缓冲区,有时则不会。有时缓冲区是硬件,有时是软件。有时它们驻留在RAM中,有时在其他内存类型中。

所以,请问更具体的问题。作为一个开始,使用维基百科,它几乎总是有用:wiki

至于代码示例,我还没有发现在getchar上刷新所有输出缓冲区的提及。文件的缓冲区通常在三种情况下刷新:

  1. fflush()或同等的
  2. 文件已关闭
  3. 缓冲区溢出。
  4. 由于这些情况都不属于您,因此不会刷新文件(请注意,此列表中的应用程序终止)。

答案 2 :(得分:1)

名词&#34;缓冲区&#34;真的是指一种用法,而不是一种独特的东西。任何存储块都可以用作缓冲区。该术语有意在一般意义上与各种I / O函数一起使用,尽管C I / O流函数的文档倾向于避免这种情况。以POSIX read()函数为例:&#34; read()尝试从文件描述符 fd读取 count 个字节 buf &#34;开始进入缓冲区。 &#34;缓冲区&#34;在这种情况下,简单地表示将记录读取的字节的存储器块;它通常实现为char[]或动态分配的块。

一个人使用缓冲区,特别是与I / O结合使用,因为某些设备(尤其是硬盘)在中型到大型块中被最有效地读取,而程序通常希望以较小的块消耗该数据。一些其他形式的I / O,例如网络I / O,可能固有地存在于块中,因此您必须记录每个整块(在缓冲区中),否则会丢失您不能立即使用的那部分。类似的考虑适用于输出。

至于你的测试程序的行为,&#34;规则&#34;您希望演示特定于控制台I / O,但只涉及其中一个流连接到控制台。