【发布时间】:2019-12-06 00:42:32
【问题描述】:
在this 的回答中,我们可以读到:
我想使用
'\n'或使用"\n"之间没有什么区别,但后者是一个(两个)字符的数组,必须逐个字符打印,必须设置循环, 这比输出单个字符更复杂。
强调我的
这对我来说很有意义。我认为输出const char* 需要一个循环来测试空终止符,必须引入比简单的putchar 更多的操作(并不意味着std::cout 与char 代表调用它 - 只是介绍一个示例的简化)。
这说服了我使用
std::cout << '\n';
std::cout << ' ';
而不是
std::cout << "\n";
std::cout << " ";
值得一提的是,我知道性能差异几乎可以忽略不计。尽管如此,有些人可能会争辩说,前一种方法的意图实际上是传递单个字符,而不是恰好是一个 char 长的字符串文字(如果你数一下'\0')。
最近我为使用后一种方法的人做了一些小的代码审查。我对这个案子做了一个小小的评论,然后继续前进。开发者随后对我表示感谢,并说他甚至没有想过这种差异(主要是关注意图)。它根本没有影响(不足为奇),但改变被采纳了。
然后我开始想到底这个变化到底有多大意义,所以我跑到了神箭那里。令我惊讶的是,在带有 -std=c++17 -O3 标志的 GCC(主干)上进行测试时,它显示了 following results。为以下代码生成的程序集:
#include <iostream>
void str() {
std::cout << "\n";
}
void chr() {
std::cout << '\n';
}
int main() {
str();
chr();
}
让我感到惊讶,因为看起来 chr() 实际上生成的指令数量是 str() 的两倍:
.LC0:
.string "\n"
str():
mov edx, 1
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
jmp 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)
chr():
sub rsp, 24
mov edx, 1
mov edi, OFFSET FLAT:_ZSt4cout
lea rsi, [rsp+15]
mov BYTE PTR [rsp+15], 10
call 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)
add rsp, 24
ret
这是为什么呢?为什么他们最终都使用const char* 参数调用相同的std::basic_ostream 函数?这是否意味着char 文字方法不仅没有更好,而且实际上比字符串文字更差?
【问题讨论】:
-
有趣的是,对于这两个版本,都调用了
char*版本的ostream::insert。 (是否存在单字符重载?)生成程序集时使用了什么优化级别? -
@3Dave 似乎没有
char过载。 GCC 和 Clang 委托给const char*过载,但 MSVS(感谢 @PaulSanders)提供了额外的 optimisation。至于优化级别,我在问题中指定了 - 我使用GCC 8.2.0和-O3。 -
考虑到您正在执行 I/O,性能差异不仅可以忽略不计,而且会降低噪音。
-
@Bohemian 我认为 OP 正在计算终止数组的空字符,正如问题后面提到的那样。
-
@Bohemian:字符串文字
"\n"的静态存储由两个字节组成:0xa(换行符)和0(终止符)。一个 2 字节的数组是一个很好的描述。 (我假设一个“正常”的 ASCII/UTF-8 C++ 实现,比如 x86-64 的 g++,其中 char = byte。)指向这个隐式长度字符串/数组的指针被传递给 ostream 运算符。
标签: c++ performance cout string-literals