printf()将输出发送到缓冲区是什么意思?

时间:2017-07-29 05:51:54

标签: c

我正在经历" C PRIMER PLUS"并且有关于" OUTPUT FLUSHING"的这个话题。 现在它说:

  

printf()语句将输出发送到称为缓冲区的中间存储。       不时地,缓冲区中的材料被发送到屏幕。该       输出从缓冲区发送到屏幕的标准C规则是       明确:

     
      
  1. 缓冲区已满时发送。
  2.   
  3. 遇到换行符时。
  4.   
  5. 当有即将到来的输入时。
  6.         

    (将缓冲区的输出发送到屏幕或文件称为刷新       缓冲区。)

现在,要验证以上陈述。我写了这个简单的程序:

#include<stdio.h>

int main(int argc, char** argv) {

printf("Hello World");

return 0;
}

所以,printf()都不包含新行,也没有一些即将发生的输入(例如scanf()语句或任何其他输入语句)。那么为什么它会在输出屏幕上打印内容。

假设第一个条件被验证为真。缓冲区已满(这根本不会发生)。 记住这一点,我将printf()中的语句截断为

printf("Hi");

仍然在控制台上打印语句。

所以这里的交易是什么,所有上述条件都是假的,但我仍然在屏幕上获得输出。 你能详细说明吗?看来我在理解这个概念时犯了一个错误。任何帮助都非常感谢。

编辑:正如一篇非常有用的评论所暗示的那样,可能在程序结束后执行exit()函数会导致所有缓冲区刷新,从而导致控制台上的输出。但是如果我们在执行exit()之前保持屏幕。像这样,

#include<stdio.h>

int main(int argc, char** argv) {

printf("Hello World!");
getchar();

return 0;
}

它仍在控制台上输出。

3 个答案:

答案 0 :(得分:4)

C标准(给定POSIX链接)中exit()的部分规范是:

  

接下来,刷新所有带有未写入缓冲数据的开放流,关闭所有打开的流,...

因此,当程序退出时,将刷新挂起的输出,而不管换行符等。同样,当文件关闭(fclose())时,会写入挂起的输出:

  

流的任何未写入的缓冲数据都将传递到主机环境以写入文件;任何未读缓冲的数据都将被丢弃。

当然,fflush()函数会刷新输出。

问题中引用的规则并不完全准确。

  1. 当缓冲区已满时 - 这是正确的。

  2. 遇到换行符时 - 虽然经常适用,但这不正确。如果输出设备是“交互设备”,则默认为线路缓冲。但是,如果输出设备是“非交互式”(磁盘文件,管道等),则输出不一定(或通常)是行缓冲的。

  3. 当有即将到来的输入时 - 这也是不正确的,尽管它通常是它的工作方式。同样,这取决于输入和输出设备是否是“交互式”。

  4. 可以通过调用setvbuf()来修改输出缓冲模式 不设置缓冲,线路缓冲或完全缓冲。

    标准说(§7.21.3):

      

    ¶3当流 unbuffered 时,字符应尽快从源或目的地出现。否则,可以将字符作为块累积并发送到主机环境或从主机环境发送。当流完全缓冲时,当填充缓冲区时,字符将作为块传输到主机环境或从主机环境传输。当流行缓冲时,当遇到换行符时,字符将作为块传输到主机环境或从主机环境传输。此外,当填充缓冲区,在无缓冲流上请求输入时,或者在需要从主机环境传输字符的行缓冲流上请求输入时,字符旨在作为块传输到主机环境。 。对这些特性的支持是实现定义的,可能会受到setbufsetvbuf函数的影响。

         

    ...

         

    ¶7在程序启动时,预定义了三个文本流,无需明确打开 - 标准输入(用于读取常规输入),标准输出(用于编写常规文本)输出),和标准错误(用于写入诊断输出)。最初打开时,标准错误流未完全缓冲;当且仅当可以确定流不参考交互设备时,标准输入和标准输出流是完全缓冲的。

    此外,§5.1.2.3程序执行说:

      
        
    • 交互设备的输入和输出动态应按照7.21.3的规定进行。这些要求的目的是尽快显示无缓冲或行缓冲输出,以确保在程序等待输入之前实际显示提示消息。
    •   

答案 1 :(得分:3)

输出缓冲是一种优化技术。将数据写入某些设备(硬盘f.e.)是一项昂贵的操作;这就是缓冲出现的原因。从本质上讲,它避免了逐字节(或char-by-char)写入数据并将其收集在缓冲区中,以便一次写入几个KiB数据。

作为优化,输出缓冲必须对用户透明(即使对程序也是透明的)。它不得影响程序的行为;无论是否缓冲(或使用不同大小的缓冲区),程序必须表现相同。这就是你提到的规则。

缓冲区只是内存中的一个区域,临时存储要写入的数据,直到有足够的数据累积,以使设备的实际写入过程高效。某些设备(硬盘等)甚至不允许以小块写入(或读取)数据,而只允许以某些固定大小的块进行写入。

缓冲区刷新规则:

  
      
  1. 缓冲区已满时发送。
  2.   

这很明显。缓冲区已满,其目的已实现,让我们将数据推送到设备。此外,可能有更多的数据来自该计划,我们需要为它腾出空间。

  
      
  1. 遇到换行符时。
  2.   

有两种类型的设备:线路模式和块模式。此规则仅适用于线路模式设备(例如,终端)。写入磁盘时刷新换行符上的缓冲区没有多大意义。但是当程序写入终端时,这样做很有意义。在终端前面,用户不耐烦地等待输出。不要让他们等太多。

但为什么输出到终端需要缓冲?在终端上写字并不昂贵。当终端物理上位于处理器附近时,这是正确的。当终端和处理器分开一半并且用户通过远程连接运行程序时,也不是这样。

  
      
  1. 当有即将到来的输入时。
  2.   

它应该显示为“当同一设备上存在阻止输入时”以使其清楚。

读取也被缓冲,原因与写作:效率相同。读取代码使用自己的缓冲区。它在需要时填充缓冲区scanf(),其他输入读取函数从输入缓冲区获取数据。

当输入即将在同一设备上发生时,必须刷新缓冲区(实际写入设备的数据)以确保一致性。程序已将一些数据发送到输出,现在它希望读回相同的数据;这就是必须将数据刷新到设备的原因,以便读取代码在那里找到并加载它。

但是为什么在应用程序退出时刷新缓冲区?

错误...缓冲是透明的,它不得影响应用程序行为。您的应用程序已将一些数据发送到输出。当应用程序退出时,数据必须在那里(在输出设备上)。

出于同样的原因,关闭相关文件时也会刷新缓冲区。这就是应用程序退出时发生的情况:清理代码关闭所有打开的文件(标准输入和输出只是从应用程序角度来看的文件),关闭强制刷新缓冲区。

答案 2 :(得分:2)

printf,缓冲的奇怪行为可以用下面的简单C代码来解释。请通读整个内容,并理解以下内容(有点棘手)

#include <stdio.h>
int main()
{
   int a=0,b=0,c=0;
   printf ("Enter two numbers");
   while (1)
   {
      sleep (1000);
   }
   scanf("%d%d",&b,&c);
   a=b+c;
   printf("The sum is %d",a);
   return 1;
}

实验1:

操作:编译并运行以上代码

观察:

预期输出为

Enter two numbers

但是看不到此输出

实验2:

操作:将Scanf语句移至while循环上方。

#include <stdio.h>
int main()
{
   int a=0,b=0,c=0;
   printf ("Enter two numbers");
   scanf("%d%d",&b,&c);
   while (1)
   {
      sleep (1000);
   }
   a=b+c;
   printf("The sum is %d",a);
   return 1;
}

观察:现在打印输出(最后在下面的原因)(仅通过scanf位置更改)

实验3:

操作:现在将\ n添加到打印语句中,如下所示

#include <stdio.h>
int main()
{
   int a=0,b=0,c=0;
   printf ("Enter two numbers\n");
   while (1)
   {
      sleep (1000);
   }
   scanf("%d%d",&b,&c);
   a=b+c;
   printf("The sum is %d",a);
   return 1;
}

观察:看到输出Enter two numbers(添加\ n后)

实验4:

操作:现在,从printf行中删除\ n,在循环,scanf行,附加行,printf行中注释掉以获取打印结果

#include <stdio.h>
int main()
{
   int a=0,b=0,c=0;
   printf ("Enter two numbers");
//   while (1)
//   {
//      sleep (1000);
//   }
//   scanf("%d%d",&b,&c);
//   a=b+c;
//   printf("The sum is %d",a);
   return 1;
}

观察:屏幕上显示“输入两个数字”行。

答案:

理查德·史蒂文斯(Richard Stevens)的书中描述了这种奇怪行为背后的原因。

打印到屏幕时打印

printf的工作是将输出写入stdout缓冲区。内核在以下情况时刷新输出缓冲区

  1. 内核需要从输入缓冲区读取内容。 (实验2)
  2. 遇到换行符时(默认情况下,因为stdout设置为 行缓冲)(实验3)
  3. 程序退出后(刷新所有输出缓冲区)(实验#4)

默认情况下,标准输出设置为行缓冲 所以printf将不会打印,因为该行没有结束。 如果没有缓冲,则所有行均按原样输出。 然后,缓冲区已满,只有在缓冲区已满时才刷新。