【问题标题】:Does new line character also flush the buffer?换行符是否也会刷新缓冲区?
【发布时间】:2017-07-14 19:36:23
【问题描述】:

我了解,endl\n 之类的问题已在 SO 上得到多次回答。但他们只提到endl 能够将缓冲区刷新到stdout,而\n 不能。

所以,我对刷新缓冲区的理解是,给定的输入存储在缓冲区中,并且仅在遇到endl 或某些显式flush 函数时才传递到stdout。如果是这样,我希望以下代码:

#include <iostream>
#include <unistd.h>

int main(void)
{
    std::cout << "Hello\nworld";
    sleep(2);
    std::cout << std::endl;

    return 0;
}

显示:

2 秒后

Hello
World

但实际输出是:

Hello

2 秒后

World

为什么会这样?

\n 不应该也存储在缓冲区中,并且仅当遇到 endl 时,缓冲区才会被刷新/显示到 stdout,但据我观察,\n 的行为方式与endl

【问题讨论】:

  • 这取决于cout 的去向。如果它进入终端(“交互式设备”),那么它不能被完全缓冲——它通常是行缓冲的,这意味着字符在打印换行符之后出现,或者理论上可以是无缓冲的。如果流向管道、文件或其他非交互目的地,endl 会强制数据输出,即使流已完全缓冲,通常也是如此。
  • 我还想知道我是否既没有提供换行符也没有提供 endl ,一旦到达程序末尾,输出是否会显示在 stdout 上,我知道它适用于终端,但它适用于所有类型的stdout吗?
  • 是的,当文件流在程序(正常)结束时关闭时,挂起的输出将被刷新。当缓冲区已满时,它也会被刷新。如果程序中止,挂起的输出通常不会被刷新。
  • @JonathanLeffler:处理交互流输出的 C++ 方法是让 std::cin 成为 tie()d 到 std::cout:每当访问 std::cin 时,std::cout 就会被刷新。对于写入stdout 的 C++ 程序,观察到行缓冲区行为的实际原因是与 stdio 的同步,显然,对于使用标准流的任何理智的 C++ 程序,该同步被禁用,否则程序会被不合理地减慢。

标签: c++ flush endl


【解决方案1】:

将 cmets 转换为答案。

这取决于cout 的去向。如果它进入终端(“交互式设备”),那么它不能被完全缓冲——它通常是行缓冲的,这意味着字符在打印换行符之后出现,或者理论上可以是无缓冲的。如果流向管道、文件或其他非交互目的地,endl 会强制数据输出,即使流已完全缓冲,通常也是如此。

我还想知道我是否既没有提供换行符也没有提供endl,一旦到达程序末尾,输出是否会显示在stdout上,我知道它适用于终端,但它是否适用到所有类型的stdout

是的,当文件流在程序(正常)结束时关闭时,挂起的输出将被刷新。当缓冲区已满时,它也会被刷新。如果程序中止,挂起的输出通常不会被刷新。

【讨论】:

    【解决方案2】:

    标准 C++ 流对象(std::cinstd::coutstd::cerrstd::clog)的默认设置是它们与相应的 C 流(stdinstdoutstderr)。同步意味着交替访问 C++ 和 C 流会产生一致的行为。例如,这段代码预计会产生字符串hello, world

    std::cout << "hel";
    fprintf(stdout, "lo,");
    std::cout << " wo";
    fprintf(stdout, "rld");
    

    C++ 标准没有规定如何实现这种同步。实现它的一种方法是禁用std::cout(和家庭)的任何缓冲并立即访问stdout。也就是说,上面的例子可以立即将单个字符写入stdout

    如果字符实际写入stdout,则将使用stdout 的缓冲模式的默认设置。我在标准中找不到规范,但通常当stdout 连接到交互式流(例如控制台)时,缓冲模式的默认值是_IOLBF,即缓冲区在最后被刷新的行。写入文件的默认值通常是_IOFBF,即,当写入一个完整的缓冲区时,输出会被刷新。因此,将换行符写入std::cout 可能会导致缓冲区被刷新。

    C++ 中的流通常设置为缓冲。也就是说,向文件写入换行符通常不会导致输出立即出现(只有在字符导致缓冲区溢出流设置为无缓冲时才会立即出现)。由于与stdout 的同步通常是不必要的,例如,当程序总是使用std::cout 写入标准输出,但确实会导致标准输出的输出非常显着减慢(禁用流缓冲会使它们slow) 可以禁用同步:

    std::ios_base::sync_with_stdio(false);
    

    这会禁用所有流对象的同步。对于一个糟糕的实现,可能没有任何效果,而一个好的实现将为std::cout 启用缓冲,从而显着提高速度,并且可能还会禁用行缓冲。

    一旦缓冲了 C++ 流,就没有内置方法可以在写入换行符时刷新它。其主要原因是处理行缓冲需要通过流缓冲区检查每个字符,这有效地抑制了对字符的批量操作,从而导致显着减速。如果需要,可以通过一个简单的过滤流缓冲区来实现行缓冲。例如:

    class linebuf: public std::streambuf {
        std::streambuf* sbuf;
    public:
        linebuf(std::streambuf* sbuf): sbuf(sbuf) {}
        int_type overflow(int_type c) {
            int rc = this->sbuf->sputc(c);
            this->sbuf->pubsync();
            return rc;
        }
        int sync() { return this->sbuf->pubsync(); }
    };
    // ...
    int main() {
        std::ios_base::sync_with_stdio(false);
        linebuf sbuf(std::cout.rdbuf());
        std::streambuf* origcout = std::cout.rdbuf(&sbuf);
    
        std::cout << "line\nbuffered\n";
    
        std::cout.rdbuf(origcout); // needed for clean-up;
    }
    

    tl;dr:C++ 标准没有行缓冲的概念,但是当标准 I/O 与 C 的 stdout 行为同步时,它可能会得到它。

    【讨论】:

      猜你喜欢
      • 2011-07-10
      • 2021-10-01
      • 2011-12-03
      • 1970-01-01
      • 2018-07-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多