“std::endl” 与 “\n”

"std::endl" vs "\n"

提问人:Head Geek 提问时间:10/18/2008 最后编辑:General GrievanceHead Geek 更新时间:6/29/2023 访问量:264393

问:

许多 C++ 书籍都包含这样的示例代码...

std::cout << "Test line" << std::endl;

...所以我也一直这样做。但是我看到了很多来自工作开发人员的代码,而不是这样的:

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

是否有技术原因偏爱其中一种,或者只是编码风格的问题?

C iostream C++-常见问题

评论

32赞 Kira 11/13/2013
@derobert这个比另一个更老
8赞 derobert 11/13/2013
@HediNaily确实如此。但另一个问题的答案让我感觉稍微好一点,所以我选择这样做。此外,另一个稍微宽一些,也覆盖了.'\n'
15赞 10/18/2008
很好的解释:cppkid.wordpress.com/2008/08/27/why-i-prefer-n-to-stdendl
0赞 qwr 3/24/2020
stackoverflow.com/a/30968225/3163618 可能存在显著的性能差异。
0赞 John Damm Sørensen 10/28/2018
如果您打算在自己的笔记本电脑以外的其他任何设备上运行您的程序,请永远不要使用该语句。特别是如果你要写很多短行,或者我经常看到一个文件是单个字符。已知使用可以杀死 NFS 等网络文件系统。endlendl

答:

50赞 Martin Beckett 10/18/2008 #1

可能存在性能问题,强制刷新输出流。std::endl

评论

1赞 dmckee --- ex-moderator kitten 10/18/2008
它可以执行本地系统所需的任何其他处理,以使其正常工作。
23赞 Ferruccio 10/18/2008 #2

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

568赞 David Thornley 10/18/2008 #3

假设文件在文本模式下打开,不同的行尾字符无关紧要,除非您要求二进制,否则您将得到该模式。编译后的程序将为编译的系统写出正确的东西。

唯一的区别是 std::endl 刷新输出缓冲区,而不是刷新输出缓冲区。如果不希望频繁刷新缓冲区,请使用 .如果这样做(例如,如果要获取所有输出,并且程序不稳定),请使用 .'\n''\n'std::endl

评论

36赞 Omnifarious 1/23/2010
或者考虑使用 instead 代替,因为它是无缓冲的,并且每次输出操作都会刷新。::std::cerr::std::cout
182赞 Martin York 2/4/2010
@Omnifarious:不应为错误保留 std::cerr。这两个流不会同步在一起,因此,如果您将一些文本输出到 cout,它可能会被缓冲,并且 cerr 将直接进入输出,这会导致混合模式显示。使用 cerr 表示它应该用于什么(错误),使用 cout 表示它的设计目的(正常交互)。
26赞 CB Bailey 2/17/2010
@Lucas:不超过“\n”是平台感知的。
37赞 Kerrek SB 11/28/2011
@LokiAstari:我不会说是“错误”。相反,如果您愿意,它适用于带外诊断消息。应该可以只说和存储真正的程序有效载荷,但程序可能希望输出更多的状态信息,即使在正常交互中也是如此。stderr./prog > file
19赞 GuLearn 8/14/2013
“在许多实现中,标准输出是行缓冲的,除非执行了 std::cout.sync_with_stdio(false),否则写入 '\n' 无论如何都会导致刷新。”从这里复制
306赞 Martin York 10/18/2008 #4

差异可以通过以下几点来说明:

std::cout << std::endl;

相当于

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

所以

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

我在大多数线路上使用。
然后在段落末尾使用(但这只是一种习惯,通常不是必需的)。
\nstd::endl

与其他声明相反,仅当流要进入文件(并且是特殊但仍然是文件(或类似文件))时,字符才会映射到行序列的正确平台末尾。\nstd::cinstd::cout

评论

6赞 C. K. Young 3/30/2011
在许多情况下,“立即查看输出”是一条红鲱鱼,因为它与 相关联,这意味着如果您从 读取输入,将首先刷新。但是,如果您想在不读取 的情况下显示进度条或其他内容,那么可以肯定的是,刷新很有用。coutcincincoutcin
21赞 legends2k 1/22/2014
@LokiAstari:如果您使用的是 << 运算符,您可能并不担心性能 - 为什么?我不知道这不是性能,或者有什么替代方法可以提高性能?请指出一些材料以进一步理解这一点。operator<<
20赞 Martin York 1/23/2014
@legends2k:有一个老太太的故事,C++流的性能不如 C printf()。虽然在某种程度上是正确的,但速度的主要差异是由于人们错误地使用 C++ 流造成的。stackoverflow.com/a/1042121/14065在 C++ 中,请记住取消将 iostream 与 C 流同步,并且不要连续刷新输出。让库确定何时这样做。stackoverflow.com/a/1926432/14065sync_with_stdio(false)
7赞 Ben Voigt 9/16/2015
@Loki:有一个都市传说,它使iostreams的速度和stdio一样快。它没有sync_with_stdio
5赞 Martin York 9/16/2015
@BenVoigt:我对上面的措辞很谨慎(所以我对他们很满意)。它的性能不如 stdio(因为它做得更多)。但是人们抱怨的很多性能差距都是由于与stdio同步造成的。
17赞 Özgür 2/22/2009 #5

没什么大不了的,但 endl 在 boost::lambda 中不起作用。

(cout<<_1<<endl)(3); //error

(cout<<_1<<"\n")(3); //OK , prints 3
40赞 Nathan 11/18/2009 #6

如果你要使用std::endl

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

a) 呼叫操作员一次。
b) 呼叫接线员两次。
<<<<

评论

34赞 smparkes 6/23/2011
这可能是显而易见的,但它对线程程序有巨大的影响,通常,第一个版本会一次性写入一行,而第二个版本可能会被其他线程的写入拆分。我经常发现自己写 std::cout << “hello\n” << std::flush 来避免这种情况。
0赞 byxor 2/23/2018
怎么样?std::cout << "Hello" << "\n";
1赞 iBug 4/19/2018
@byxor 除了其他答案中描述的缓冲区冲洗外,几乎相同。无论如何,当您可以将两个字符串文字合并为一个时,它是多余的。
0赞 Enlico 11/22/2018
好吧,如果要打印的字符串不是文字,那么在案例 a 中对 的调用也将是 2,因此我不会声称需要一两个(或通常两个函数调用)是 和 之间的差异。<<<<\nendl
0赞 Carlo Wood 2/27/2019
哈哈,不,这不是我使用 \n 的原因。
15赞 smerlin 2/17/2010 #7

如果你使用 Qt 和 ,你可能会不小心使用不正确的结果,这给你带来了非常令人惊讶的结果。请参阅以下代码片段:endlendl

#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();
}

请注意,我写了而不是(这本来是正确的),显然在 qtextstream.h 中定义了一个函数(它是 QtCore 的一部分)。endlstd::endlendl

使用而不是完全回避任何潜在的命名空间问题。 这也是一个很好的例子,为什么将符号放入全局命名空间(就像Qt默认所做的那样)是一个坏主意。"\n"endl

评论

40赞 Steve Folly 2/17/2010
呃!谁会想成为??:-)using namespace std;
3赞 Head Geek 2/25/2010
讨厌。感谢您的评论,我相信其他人会遇到这种情况。
1赞 ʇolɐǝz ǝɥʇ qoq 3/22/2015
@SteveFolly我愿意,为什么不呢?
0赞 smerlin 3/31/2015
@ʇolɐǝzǝɥʇqoq 只要您不在头文件中这样做就可以了。
7赞 L. F. 7/22/2019
@ʇolɐǝzǝɥʇqoq 请避免 .这被认为是不好的做法。请参阅为什么“使用命名空间标准”被认为是不良做法?using namespace std;
36赞 Emily L. 8/29/2014 #8

我记得在标准中读到过这个,所以这里是:

请参阅 C11 标准,该标准定义了标准流的行为方式,因为 C++ 程序与 CRT 连接,因此 C11 标准应在此处管理刷新策略。

ISO/IEC 9899:201x 认证

7.21.3 第 7 节

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

7.21.3 第 3 节

当流未缓冲时,字符应从源或 尽快到达目的地。否则,字符可能会累积,并且 作为块传输到主机环境或从主机环境传输。当流完全缓冲时, 字符旨在作为块传输到主机环境或从主机环境传输,当出现以下情况时 缓冲区已填充。当流被行缓冲时,字符应为 当换行符 遇到。此外,字符旨在作为块传输到主机 环境,当缓冲区被填满时,当在无缓冲的流上请求输入时,或者 当在需要传输的线路缓冲流上请求输入时 主机环境中的字符。对这些特征的支持是 实现定义,并且可能受 setbuf 和 setVBUF 函数的影响。

这意味着当且仅当它们引用非交互式设备时,它们才被完全缓冲。换句话说,如果 stdout 附加到终端,则行为没有区别。std::coutstd::cin

但是,如果被调用,则即使对交互式设备也不会造成刷新。否则等同于除非通过管道连接到文件:std::endl 上的 c++ refstd::cout.sync_with_stdio(false)'\n''\n'std::endl

8赞 Kaleem Ullah 3/27/2018 #9

参考:这是一个仅输出的 I/O 操纵器

std::endl在输出序列 os 中插入换行符,并像调用后跟 .os.put(os.widen('\n'))os.flush()

何时使用:

该机械手可用于立即产生一行输出

例如

显示长时间运行的进程的输出时,记录多个线程的活动或记录可能意外崩溃的程序的活动。

如果生成的进程执行任何屏幕 I/O,则在调用 std::system 之前,还需要显式刷新 std::cout。在大多数其他常见的交互式 I/O 方案中,std::endl 与 std::cout 一起使用时是多余的,因为来自 std::cin 的任何输入、到 std::cerr 的输出或程序终止都会强制调用 std::cout.flush()。在某些来源的鼓励下,使用 std::endl 代替 '\n' 可能会显著降低输出性能。

10赞 TheHardew 8/7/2021 #10

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

#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:

请注意,由于是一个字符,填充宽度设置为 2,因此之前只打印了 1 个零。'\n''\n'

我在任何地方都找不到关于它的任何信息,但它使用 clang、gcc 和 msvc 重现。

当我第一次看到它时,我非常困惑。

评论

0赞 leoleosuper 5/5/2022
这是由于 Windows 级别的实现,其中 \n 只是换行符,而 endl 是回车符 (\r) 和换行符 (\n),总共 \r\n。正如其他人指出的那样,\n 实际上应该给出 \r\n 如果流要进入文件。
0赞 TheHardew 5/5/2022
@leoleosuper这在 linux 上使用 / 的行为方式相同(我没有在 Windows 上测试过)。在 linux 上传递时既不产生回车。你回答了错误的答案吗?你说“就像其他人指出的那样”,但你是这里唯一的评论。coutofstreamofstreamendl\nxxd
0赞 leoleosuper 5/9/2022
我的意思是对这些答案的其他答案/评论指出了这一点。我想我想通了:把“\n”放在 cout 上,因此,setfill 将填充之前的角色。endl 不会通过 cout 来放置字符,而是直接放置它,然后刷新输出。因此,在您的设置中,cout << '\n' 会将输入字符串修改为 “0\n”,然后使用 os.put(“0\n”) 来放置 0 和换行符。另一方面,endl 直接转到 os.put('\n'),忽略 setfill。
1赞 Troubadour 4/10/2023
@leoleosuper 这并不是依赖于平台。“\n”的格式化输出是依赖于平台的内容。在 Windows 上,“\n”的格式输出也会给出回车符。我怀疑在 Windows 上输出不会显示 0。有趣的是,如果宽度设置为 1,它在 Windows 上会做什么。endl
-1赞 jonathask 6/29/2023 #11

来自 GCC 文档

有些人还认为,将 endl 发送到输出流只会写一个换行符。这是不正确的;写入换行符后,缓冲区也会被刷新。也许这就是你在写到屏幕时想要的效果 - 尽快把文本拿出来,等等 -- 但是当对文件这样做时,缓冲在很大程度上被浪费了:

output << "a line of text" << endl;
output << some_data_variable << endl;
output << "another line of text" << endl; 

在这种情况下,正确的做法是将数据写出,让库和系统担心缓冲。如果你需要换行符,只需写一个换行符:

output << "a line of text\n"
<< some_data_variable << '\n'
<< "another line of text\n"; 

您可以检查ostream的文档,或检查实现本身 - 在我的案例中,usr/include/c++/11/ostream:684 -。在那里你会发现:endl

  // Standard basic_ostream manipulators

  /**
   *  @brief  Write a newline and flush the stream.
   *
   *  This manipulator is often mistakenly used when a simple newline is
   *  desired, leading to poor buffering performance.  See
   *  https://gcc.gnu.org/onlinedocs/libstdc++/manual/streambufs.html#io.streambuf.buffering
   *  for more on this subject.
  */
  template<typename _CharT, typename _Traits>
    inline basic_ostream<_CharT, _Traits>&
    endl(basic_ostream<_CharT, _Traits>& __os)
    { return flush(__os.put(__os.widen('\n'))); }