我知道像endl
和n
之间的差异这样的问题已经在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::cin
、std::cout
、std::cerr
和std::clog
)的默认设置是它们与相应的 C 流(stdin
、stdout
和stderr
)同步。同步意味着交替访问 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
。