换行符是否也刷新缓冲区?

Does new line character also flush the buffer?

我知道像 endl\n 之间的区别这样的问题已经在 SO 上回答了很多次。但他们只提到 endl 能够将缓冲区刷新到 stdout,而 \n 则不能。

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

#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 时,缓冲区才会 flushed/displayed 到 stdout,但是从我的角度来看观察 \nendl 的行为方式相同。

正在将评论转化为答案。

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

I also wanted to know if I provided neither new line character nor endl, will the output be displayed on the stdout once it reaches the end of the program, I know it does for terminal, but is it applicable to all types of stdout?

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

标准 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 写入标准输出,但确实导致标准输出的输出速度显着降低(禁用缓冲流使它们变慢) 可以禁用同步:

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.

行为同步时,它可能会得到它