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



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

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

#include <iostream>
#include <unistd.h>
int main(void)
{
std::cout << "Hellonworld";
sleep(2);
std::cout << std::endl;
return 0;
}

要显示:

2 秒后

Hello
World

但实际输出是:

Hello

2 秒后

World

为什么会这样?

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

将注释转换为答案。

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

我还想知道如果我既没有提供换行符也没有提供endl,输出一旦到达程序末尾就会显示在stdout上,我知道它适用于终端,但它适用于所有类型的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 << "linenbufferedn";
std::cout.rdbuf(origcout); // needed for clean-up;
}

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

最新更新