是否将单字符字符串文字优化为简单的字符类型文字?

时间:2013-03-31 00:45:55

标签: c++ char newline string-literals

通常在使用C ++进行编码时,我会使用换行符cout)结束\n语句。但是,我的直觉一直是将这个换行符表示为字符串文字:"\n",即使它是单个字符,并且可以更有效地表达为字符文字('\n')。 例如:

cout << "The value of var is " << var << "\n";

有这种现象的很多代码。所以,问题如下:

  1. 表达换行符号常量的两种不同方式的效率是否有任何差异?我不担心在制作节目的执行方面有任何实际的差异(我认为这是微不足道的);相反,它只是让我觉得有些效率,无论多么微小,都可能无缘无故地丢失。

  2. 如果string-literal版本的效率较低,编译器是否会将其优化为字符常量版本,因为两者提供完全相同的行为?

  3. 我也熟悉std::endl。文档说“当需要简单的换行符时,这种操纵器经常被错误地使用,导致缓冲性能差。”并指向this article以获取更多信息。但是,该文章指出所提到的“性能不佳”仅适用于文件I / O,并且使用endl写入屏幕实际上可能会提高性能。这有什么用?

  4. 我搜索过C ++标准库但找不到<<运算符的相关重载的实现。我在ostream.tcc中找到了声明:

    extern template ostream& operator<<(ostream&, char);
    extern template ostream& operator<<(ostream&, const char*);
    

    但是没有关于机制如何在实施中落实的线索。

    这更像是一个理论问题,所以我对阅读“两者之间没有 pratical 差异感兴趣”。我知道。我只是想知道是否存在任何差异以及编译器如何处理它。

3 个答案:

答案 0 :(得分:3)

它们可能优化为一个字符串(每个编译单元) - 大多数编译器将“合并相同内容的字符串”。

除了将指针传递给单个字符串之外,我希望实际上没有什么实际差别。

提出具体问题:

  1. 是的,稍有不同,因为char *需要一些间接,因此会生成一些额外的指令来执行。对于控制台输出(而不是输出到文件),这并不重要,因为滚动控制台,即使在全屏文本模式下也是如此。多100个指令。
  2. 怀疑它。
  3. 因此std::endl将刷新缓冲区,这确实会减少输出到文件,因为正在将部分扇区或块写入文件,这会增加系统调用开销。如果使用"\n",则在填充缓冲区本身之前不会刷新文件,该缓冲区本身至少为512字节,可能高达数十千字节。但是对于答案#1,控制台输出性能将更加依赖于屏幕可以滚动的速度。

答案 1 :(得分:2)

字符串文字\nendl之间的差异是:

\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'情况稍微复杂一些,因为角色必须放在堆栈上。)