2016-09-16 35 views
5

我只是很好奇,应该满足哪些条件来自动刷新标准输出缓冲区。C中自动刷新标准输出缓冲区的规则是什么?

首先我很困惑,这个伪代码不会打印每一个迭代输出:

while (1) { 
    printf("Any text"); 
    sleep(1); 
} 

但是,如果我添加换行符会的。

几个实验后,我发现,我的机器标准输出缓冲区被刷新:

  1. 当我把到stdout 1025个以上的字符;
  2. 当我阅读stdin;
  3. 当我把换行符放到标准输出中时;

第一个条件是完全清楚的 - 当缓冲区满了,它应该被刷新。第二个也是合理的。但为什么换行符会导致冲洗?其他的隐含条件是什么?

+0

我最近被告知(通过@chux)第三个条件不是在C标准中,而是被定义的实现,和前两个一样。 –

+0

你可以看看[http://stackoverflow.com/questions/13932932/why-does-stdout-need-explicit-flushing-when-redirected-to-file#13933741](http://stackoverflow.com/questions/13932932/why-do-stdout-need-explicit-flush-when-redirected-to-file#13933741) – owacoder

+0

@WeatherVane:我想你误会了。对于线路缓冲输出流,行为*是C所必需的,但stdout不需要行缓冲,除非它是交互式设备。 –

回答

6

自动清洗标准输出缓冲区的规则是实现定义的(ID)。当流为无缓冲,全缓冲行缓冲时为ID。

当流是无缓冲,字符旨在尽快从源或目的地出现。否则,字符可能会累积并作为块向主机环境或从主机环境传输。

当一个数据流是完全缓冲,字符是打算作为一个块填充缓冲区时发送到主机环境或从主机环境传输。

当一个流为行缓冲,当遇到换行符时,打算将字符作为块传送到主机环境或从主机环境传送出去。此外,当填充缓冲区,当在非缓冲流上请求输入时,或者在需要从主机环境传输字符的线路缓冲流上请求输入时,字符旨在作为块被传输到主机环境。

支持这些特性是实现定义,... C11dr§7.21.33


我只是好奇,哪些情况应满足自动刷新标准输出缓冲区。

如果代码要确保输出肯定是刷新的,请使用fflush()。可能会自动刷新流的其他条件是已定义的实现。

3
  • 无论何时输出换行符,都应刷新行缓冲的输出流。

  • 只要尝试从任何行缓冲输入流读取,实现可能(但不是必需)刷新所有行缓冲输出流。

  • 除非可以确定它们不与“交互式设备”关联,否则实现不允许默认使流完全缓冲。所以当stdin/stdout是终端时,它们不能被完全缓冲,只能是行缓冲(或无缓冲)。

如果您只需要在输出到终端时进行刷新,就足以假设写入换行符会导致刷新。否则,您应该在需要冲洗的地方明确呼叫fflush

+0

谢谢。这很明显。 –

3

请参阅the man page for setbuf(3)。默认情况下,stdout设置为行缓冲模式。

printf()及其变体与缓冲输出一起使用,并委托给write()。因此,此缓冲由C++库实现printf控制,缓冲区和缓冲区设置位于FILE结构中。

值得注意的是unix手册页的第3部分和第2部分之间的区别。第2部分由直接与操作系统交流的函数调用组成,并且执行纯粹的用户程序无法做到的事情。第3部分由用户可以自行复制的函数调用组成,通常委托给第2部分的调用。第2节的功能包含低级的“魔术”,它允许C程序与外部世界进行交互并执行I/O。第3节功能可以为第2节功能提供更方便的界面。

printfscanfgetcharfputs,以及其他功能FILE *都是第3个函数委托write()read(),它们是部2层的功能。read()write()不缓冲。 printf()FILE结构中的缓冲区交互,偶尔会决定通过write()将该缓冲区的内容发送出去。

+1

谢谢。这真的很有用。我不知道这些不同类型的缓冲区。这个手册页解释了一切。 –

+0

我写了一个简短的答案,然后更详细地编辑,以防您错过了。 – NovaDenizen

+0

哦,是的,我只看到你的评论后:)但无论如何,该链接是关于这个对我来说最有用的信息。我知道那些男人的区别,但也许对别人有用。谢谢。 –

1

有很多情况下,当一个流缓冲输出自动刷新:

  1. 当您尝试做输出和输出缓冲区已满。
  2. 流关闭时。
  3. 当程序通过调用exit退出。
  4. 当写入换行符时,如果流被行缓存。
  5. 每当任何流上的输入操作实际上从其文件读取数据时。

stdout默认是行缓冲。

如果你想在另一次刷新缓冲输出,你可以调用fflush。

0

Online C2011 standard

7.21.3文件
...
       当流是 无缓冲,字符旨在从源或在 目的地出现尽快地。否则,字符可能会累积并作为一个块传输到主机环境或从主机环境传输。当一个流是完全缓冲的 , 字符是打算作为一个块被发送到或从主机环境作为一个块时填充缓冲区。 当一个流为行缓存时,当遇到新行字符为 时,字符意图为 作为块传输到主机环境或从主机环境传输。此外,当缓冲区被填充时,当在非缓冲流上请求输入时,字符被打算作为块被传送到主机 环境,或者当缓冲流请求输入时,字符被从 字符发送到 主机环境。对这些特性的支持是 实现定义的,并可能通过 setbufsetvbuf函数受到影响。
...
       程序启动时,三个文本流是预定义的,不需要明确地打开 — 标准输入(用于读取传统的输入), 标准输出(写 传统输出)和 标准错误(用于写入诊断输出)。最初打开 ,标准错误流没有完全缓冲; 标准输入和标准 输出流被完全缓冲当且仅当该流可以被确定为不将 引用到交互设备。

因此,行缓冲流将刷新换行。在我有经验的大多数系统中,stdout在交互式会话中是行缓冲的。