通常在使用C ++进行编码时,我会使用换行符cout
)结束\n
语句。但是,我的直觉一直是将这个换行符表示为字符串文字:"\n"
,即使它是单个字符,并且可以更有效地表达为字符文字('\n'
)。
例如:
cout << "The value of var is " << var << "\n";
有这种现象的很多代码。所以,问题如下:
表达换行符号常量的两种不同方式的效率是否有任何差异?我不担心在制作节目的执行方面有任何实际的差异(我认为这是微不足道的);相反,它只是让我觉得有些效率,无论多么微小,都可能无缘无故地丢失。
如果string-literal版本的效率较低,编译器是否会将其优化为字符常量版本,因为两者提供完全相同的行为?
我也熟悉std::endl
。文档说“当需要简单的换行符时,这种操纵器经常被错误地使用,导致缓冲性能差。”并指向this article以获取更多信息。但是,该文章指出所提到的“性能不佳”仅适用于文件I / O,并且使用endl
写入屏幕实际上可能会提高性能。这有什么用?
我搜索过C ++标准库但找不到<<
运算符的相关重载的实现。我在ostream.tcc
中找到了声明:
extern template ostream& operator<<(ostream&, char);
extern template ostream& operator<<(ostream&, const char*);
但是没有关于机制如何在实施中落实的线索。
这更像是一个理论问题,所以我对阅读“两者之间没有 pratical 差异感兴趣”。我知道。我只是想知道是否存在任何差异以及编译器如何处理它。
答案 0 :(得分:3)
它们可能优化为一个字符串(每个编译单元) - 大多数编译器将“合并相同内容的字符串”。
除了将指针传递给单个字符串之外,我希望实际上没有什么实际差别。
提出具体问题:
char *
需要一些间接,因此会生成一些额外的指令来执行。对于控制台输出(而不是输出到文件),这并不重要,因为滚动控制台,即使在全屏文本模式下也是如此。多100个指令。 std::endl
将刷新缓冲区,这确实会减少输出到文件,因为正在将部分扇区或块写入文件,这会增加系统调用开销。如果使用"\n"
,则在填充缓冲区本身之前不会刷新文件,该缓冲区本身至少为512字节,可能高达数十千字节。但是对于答案#1,控制台输出性能将更加依赖于屏幕可以滚动的速度。 答案 1 :(得分:2)
字符串文字\n
和endl
之间的差异是:
\n
是一个附加到stdout的字符串文字。
endl
也会将换行符附加到stdout,但是,它也会刷新stdout缓冲区。因此,可能需要更多处理。除此之外,应该没有实际的区别。
答案 2 :(得分:1)
我强烈怀疑它,因为它改变了内存布局(一个具有空终止符,另一个没有),并且因为它将涉及更改文本的实际类型(并且,通过扩展,更改函数)被称为)。因此,在绝大多数情况下,这将是一种无效的转变,而在极少数情况下,这种转变还不足以为数不胜数。
也就是说,如果编译器进行了足够的积极内联(将函数本身和函数本身内联到函数中),那么最终可能会得到相同的代码。例如,Clang编译以下内容:
#include <iostream>
using namespace std;
int main() {
cout << "X" << "\n";
cout << "Y" << '\n';
}
进入这个:
movq std::cout@GOTPCREL(%rip), %rbx
leaq L_.str(%rip), %rsi
movq %rbx, %rdi
movl $1, %edx
callq std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
leaq L_.str1(%rip), %rsi
movq %rbx, %rdi
movl $1, %edx
callq std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
leaq L_.str2(%rip), %rsi
movq %rbx, %rdi
movl $1, %edx
callq std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
leaq -9(%rbp), %rsi
movb $10, -9(%rbp)
movq %rbx, %rdi
movl $1, %edx
callq std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
xorl %eax, %eax
addq $8, %rsp
popq %rbx
popq %rbp
正如您所看到的,内联使两个案例几乎完全相同。 (事实上,'\n'
情况稍微复杂一些,因为角色必须放在堆栈上。)