"std::endl"与"\n"的区别

707
许多C++书籍都包含像这样的示例代码...
std::cout << "Test line" << std::endl;

...所以我一直都是这样做的。但我看到很多工作开发人员的代码却是这样的:

std::cout << "Test line\n";

你认为是出于技术原因还是仅仅是编码风格的选择?


15
好的解释:http://cppkid.wordpress.com/2008/08/27/why-i-prefer-n-to-stdendl/ - Payton Millhouse
32
@derobert 这个比另一个旧。 - Kira
8
@HediNaily确实是这样。但是,另一个答案让我觉得略微更好,所以我选择采用那种方式。而且,另一个答案的范围略微更广,也包括'\n' - derobert
如果你打算在自己的笔记本电脑以外的任何设备上运行程序,绝对不要使用 endl 语句。特别是当你将许多短行或像我经常看到的单个字符写入文件时。使用 endl 已知会破坏像 NFS 这样的网络文件系统。 - John Damm Sørensen
可能会有显著的性能差异。 - qwr
11个回答

568

假设文件以文本模式打开,不同的换行符并不重要,这是默认的模式,除非你要求使用二进制模式。编译后的程序会为所编译的系统写出正确的内容。

唯一的区别在于std::endl会刷新输出缓冲区,而'\n'则不会。如果你不想频繁地刷新缓冲区,请使用'\n'。如果你需要(例如,如果你想获取所有的输出,并且程序不稳定),请使用std::endl


36
考虑使用::std::cerr代替::std::cout,因为它是无缓冲的,并且每次输出操作都会刷新。 - Omnifarious
182
@Omnifarious:不应该在 std::cerr 中输出非错误信息。这两个流没有同步,因此如果您将一些文本输出到 cout,它可能会被缓冲,并且 cerr 将直接输出到输出结果,从而导致混合模式显示。请按照它们的设计意图使用 cerr(用于错误)和 cout(用于正常交互)。 - Martin York
26
@Lucas:除了 '\n' 之外,没有平台感知性。 - CB Bailey
37
@LokiAstari说:我不会说stderr是用于“错误”的。相反,它是用于带外诊断消息的。如果可以说./prog > file并仅存储真正的程序有效负载,则应该可以,但是即使在正常交互中,程序可能也需要输出更多的状态信息。 - Kerrek SB
19
在许多实现中,标准输出采用行缓冲方式,写入 '\n' 会导致刷新操作,除非执行了 std::cout.sync_with_stdio(false)。 - GuLearn
显示剩余8条评论

306

下面的例子可以说明这个差异:

std::cout << std::endl;

等同于

std::cout << '\n' << std::flush;

因此,

  • 如果您想强制立即刷新输出,请使用std::endl
  • 如果您担心性能问题(如果您正在使用<<运算符,则可能并非如此),请使用\n

我在大多数行上使用\n
然后在段落末尾使用std::endl(但这只是一种习惯而通常不必要)。

与其他说法相反,\n字符仅在流将写入文件时才映射到正确的平台换行符序列(std::cinstd::cout特殊但仍然是文件或类似文件)。


6
在许多情况下,“立即看到输出”是一个误导,因为coutcin相绑定,这意味着如果你从cin读取输入,那么cout将首先被清空。但是,如果你想要显示进度条或其他内容而不是从cin读取,那么刷新是有用的。 - C. K. Young
21
如果你正在使用"<<运算符",那么你可能不太关心性能——为什么?我不知道operator<<不够高效,或者有哪些替代方案可以提高性能?请指引我查找更多相关资料来进一步了解此问题。 - legends2k
20
有一个老传说认为,C++流不如C语言printf()函数快。虽然在一定程度上是正确的,但主要的速度差异是由于人们错误地使用C++流。在C++中,请记得使用“sync_with_stdio(false)”取消iostream和C-stream之间的同步,并且不要连续刷新输出。让库自行决定何时刷新。 - Martin York
7
有一个城市传说,声称 sync_with_stdio 可以让 iostreams 的速度像 stdio 一样快。但事实并非如此,详情请参见 https://dev59.com/W-o6XIcBkEYKwwoYTS1D。 - Ben Voigt
5
@BenVoigt: 我在上面措辞非常谨慎(所以我对它们感到满意)。 它的性能不如stdio(因为它做得更多)。 但是,人们抱怨的很多性能差距都是由于与stdio同步造成的。 - Martin York
显示剩余2条评论

50

使用std::endl会强制刷新输出流,可能导致性能问题。


1
它可以执行本地系统需要的任何其他处理,以确保其良好运行。 - dmckee --- ex-moderator kitten

40

如果你要使用 std::endl,那么在其中还有另一个函数调用被隐含了。

a) std::cout << "Hello\n";
b) std::cout << "Hello" << std::endl;

a) 调用一次运算符 <<
b) 调用两次运算符 <<


34
显而易见的是,这对于多线程程序具有巨大的影响,一般情况下,第一个版本会在一次写入中写入单行,而第二个版本可能会被其他线程的写入拆分。经常发现自己编写 std::cout << "hello\n" << std::flush 以避免这种情况。 - smparkes
std::cout << "Hello" << "\n";是什么意思? - byxor
1
@byxor,除了其他答案中描述的缓冲刷新之外,几乎相同。无论如何,当您可以将两个字符串文字合并为一个时,它是多余的。 - iBug
如果要打印的字符串不是字面量,则在情况_a_中,对<<的调用也将是2次,因此我不会声称需要一个或两个<<(或两个函数调用总体上)是\nendl之间的区别。 - Enlico
不,那不是我使用 \n 的原因。 - Carlo Wood

35

我记得在标准中读到过这个问题,下面是翻译:

参考C11标准,该标准定义了标准流的行为。由于C++程序使用CRT接口,因此应该遵循C11标准来控制刷新策略。

ISO/IEC 9899:201x

7.21.3 §7

在程序启动时,三个文本流被预定义,并且不需要显式打开——标准输入(用于读取常规输入)、标准输出(用于写入常规输出)和标准错误(用于写入诊断输出)。标准错误流最初不是完全缓冲的;当且仅当可以确定流不引用交互设备时,标准输入和标准输出流才是完全缓冲的。

7.21.3 §3

当流是无缓冲的时,字符会尽快地从源或目的地出现。否则,字符可能会被累积并作为一个块传输到或从主机环境中。当流是完全缓冲的时,字符被传输到主机环境中当缓冲区被填满时。当流是行缓冲的时,当遇到换行符时,字符被传输到或从主机环境中作为一个块。此外,当流满足以下条件之一时,字符被传输作为一个块到主机环境:缓冲区被填满时、在无缓冲流上请求输入时,或者在需要传输主机环境中的字符的行缓冲流上请求输入时。对这些特性的支持是实现定义的,并且可以通过setbuf和setvbuf函数进行影响。

这意味着std::coutstd::cin仅在引用非交互设备时才是完全缓冲的。换句话说,如果stdout连接到终端,则没有任何行为上的区别。

然而,如果调用std::cout.sync_with_stdio(false),那么即使对于交互设备,'\n'也不会导致刷新。否则,'\n'std::endl等效,除非将其管道传输到文件:关于std::endl的c++参考文献


23

它们都会写入适当的换行符。此外,使用endl会导致缓冲区提交。在进行文件I/O时通常不希望使用endl,因为不必要的提交可能会影响性能。


16

14

如果你使用Qt和endl,你可能会意外地使用错误的endl,从而导致非常令人惊讶的结果。请参见以下代码片段:

#include <iostream>
#include <QtCore/QtCore> 
#include <QtGui/QtGui>

// notice that there is no "using namespace std;"
int main(int argc, char** argv)
{
    QApplication qapp(argc,argv);
    QMainWindow mw;
    mw.show();
    std::cout << "Finished Execution!" << endl;
    // This prints something similar to: "Finished Execution!67006AB4"
    return qapp.exec();
}
请注意,我写了 endl 而不是 std::endl(后者本应正确),显然有一个 endl 函数在 qtextstream.h 中定义(这是 QtCore 的一部分)。
使用 "\n" 代替 endl 完全避开了任何潜在的命名空间问题。 这也是为什么将符号放入全局命名空间(像Qt默认情况下所做的那样)是一个坏主意的很好的例子。

40
呃!谁会想要使用 using namespace std; 呢? :-) - Steve Folly
3
讨厌。感谢您的评论,我相信其他人也会遇到这个问题。 - Head Geek
1
@SteveFolly 我是这样做的。为什么不呢? - ʇolɐǝz ǝɥʇ qoq
@ʇolɐǝzǝɥʇqoq 只要不在头文件中这样做就可以了。 - smerlin
7
请避免使用 using namespace std;,这被认为是一种不好的编程习惯。请参考 为什么“using namespace std;”被认为是不好的编程习惯? - L. F.

10

我从未见过有人说过 '\n' 受 cout 格式化的影响:

#include <iostream>
#include <iomanip>

int main() {
    std::cout << "\\n:\n" <<  std::setw(2) << std::setfill('0') << '\n';
    std::cout << "std::endl:\n" << std::setw(2) << std::setfill('0') << std::endl;
}

输出:

\n:
0
std::endl:


注意,由于'\n'只有一个字符且填充宽度为2,因此在'\n'之前只打印了1个零。
我无法在任何地方找到关于此的信息,但它可以在clang、gcc和msvc中再现。
我第一次看到时非常困惑。

这是由于Windows级别的实现,其中\n只是换行符,而endl是回车(\r)和换行(\n),总共为\r\n。正如其他人指出的那样,如果流要写入文件,\n实际上应该给出\r\n。 - leoleosuper
@leoleosuper 在Linux上使用 cout/ofstream 时,它的行为与此相同(我尚未在Windows上测试ofstream)。无论是endl还是\n都不能在通过Linux上的xxd时产生回车。你是不是回复错了答案?你说“像其他人指出的那样”,但这里只有你一个评论。 - TheHardew
我的意思是其他答案/评论指出了这一点。我想我弄清楚了:在cout中放置'\n',因此setfill将填充字符之前。endl不通过cout放置字符,而是直接放置它,然后刷新输出。因此,在您的设置中,cout << '\n'将修改输入字符串为“0\n”,然后使用os.put(“0\n”)以放置0和换行符。另一方面,endl直接进入os.put('\n'),忽略setfill。 - leoleosuper
1
@leoleosuper,与平台有关的不是 endl。格式化输出的 '\n' 才是与平台有关的东西。在 Windows 上,'\n' 的格式化输出也会给出回车符。我怀疑,在 Windows 上输出不会显示 0。有趣的是,如果宽度设置为 1,它在 Windows 上会做什么。 - Troubadour

8
使用引用,这是一个仅供输出的I/O操作符std::endl将换行符插入到输出序列中并刷新它,就像通过调用os.put(os.widen('\n'))后紧接着调用os.flush()来进行一样。
在以下情况下使用:
该操作符可用于立即生成输出行
例如:显示长时间运行进程的输出、记录多个线程的活动或对可能会意外崩溃的程序的活动进行记录。
此外
如果派生进程执行任何屏幕I/O,则在调用std::system之前也需要显式刷新std::cout。在大多数其他常见的交互I/O方案中,与std::cout一起使用std::endl是多余的,因为std::cin的任何输入、std::cerr的任何输出或程序终止都会强制调用std::cout.flush()。某些来源鼓励使用std::endl代替'\n',但这样可能会严重降低输出性能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接