为什么 cerr 会刷新 cout 的缓冲区

7
#include <iostream>
using std::cout;
using std::endl;
using std::cerr;
#include <cstdio>

int   main( )
{
    char pbuffer[BUFSIZ];
    setbuf(stdout, pbuffer);
    cout << "hello cout" ;
    sleep(5);
    cerr << "hello cerr";
    sleep(5);
    cout << "\nAll   done " << endl;
    sleep(5);
    return 0;
}

编译并运行上述程序后,它的输出如下:

hello couthello cerr
All   done 

我认为应该是这样的:

但我认为它应该是:

hello cerrhello cout
All   done 

我想知道为什么cerr会刷新cout的缓冲区?

cout << "hello cout";cerr << "hello cerr"; 之前。 - iammilind
我想知道为什么 cout 的输出在 cerr 之前。我认为 cout 的输出是缓冲的,cerr 的输出应该先出现。 - wildpointercs
2个回答

12

这是有意为之的。

cincerr 都与 cout 相关联,并在它们自己的操作之前调用 cout.flush()。

这个想法很可能是希望输入和输出按照正确的顺序进行。


这是错误的。coutcin绑定,但仅限于此。cerr没有绑定任何东西,也没有任何东西绑定到它上面。(这并不意味着实现不使用其他同步机制。但是,cerr.tie()需要返回一个空指针。) - James Kanze
快速检查显示VC8在这里存在错误:cerr.tie()返回非空,尽管C++标准明确禁止这样做。(g++则正确处理此问题。) - James Kanze
@James - 在我的草稿副本中写着:“在对象cerr初始化之后,cerr.flags() & unitbuf不为零且cerr.tie()返回&cout。”(27.4.2) - Bo Persson
我正在查看实际的ISO C++ 2003标准,其中写道“在初始化对象cerr之后,cerr.flags()&unitbuf不为零。否则,它的状态与basic_ios <char> :: init所需的状态相同,其中要求tie() == 0。”我看到后来的草案已经改变了这一点——又一个需要处理的不兼容性。(然而,这不是一个严重的问题,因为据我所见,cout和cerr实际上可以共享同一个streambuf,这样可以更加同步。) - James Kanze
1
cerr 现在与 cout 相关联 - 引用 https://en.cppreference.com/w/cpp/io/cerr - 此外,std::cerr.tie() 返回 &std::cout(对于 wcerrstd::wcout 也是如此),这意味着任何对 std::cerr 的输出操作首先执行 std::cout.flush()(通过 std::basic_ostream::sentry 的构造函数)(自 C++11 起)。 - aafulei
对于历史上的相关信息:曾经有一次委员会在LWG 178中拒绝了将cerrcout绑定的行为更改请求,但之后 WG21 N1569 指出它应该被更改。接着提交了LWG 455。最后修复方案于2005年收录在工作文稿中。 - FrankHB

11
首先,流可以在它觉得需要时刷新。某些iostream的实现可能会在向交互设备输出时更改缓冲策略。除非您有意在两个流之间刷新,否则它们出现的顺序是没有指定的;您能确定的是单个<<cerr不会插入来自cout的字符。在您的情况下,实现以某种方式同步coutcerr。(您可能想看看将它们的输出重定向到不同的文件会发生什么。或者将其重定向到相同的非交互式文件-C++在交互式设备和其他设备之间没有区别,但C有,我希望大多数C++实现在这方面遵循C的做法。)
顺便说一下,关于顺序的两个保证是:
  • cout绑定到cin,因此任何尝试在cin上读取的操作都会刷新cout
  • cerr设置了unitbuf,因此它将在每个<<运算符结束时被刷新。
后一种方法背后的思想是,我认为是为了获得类似于C的行缓冲,而C++不直接支持-虽然如果您使用std::endl,则可以获得与行缓冲相同的效果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接