14

我了解诸如 SO 之间的区别endl\n已多次回答的问题。但他们只提到endl能够将缓冲区刷新到stdout,而\n,则不能。

因此,我对缓冲区被刷新的理解是,给定的输入存储在缓冲区中,并且stdout仅在遇到endl或某些显式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遇到缓冲区时才会刷新/显示到 上stdout,但据我观察\n,其行为方式与endl.

4

2 回答 2

9

将评论转换为答案。

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

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

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

于 2017-02-24T04:59:49.457 回答
6

std::cin标准 C++ 流对象( 、std::coutstd::cerr和)的默认设置std::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.

于 2017-02-24T05:04:04.280 回答