考虑以下代码:
bool isFoo(const char* bar) {
return !strcmp(bar, "some_long_complicated_name");
}
在这里,字符串文字"some_long_complicated_name"
立即传递给strcmp
。这是否意味着每次调用isFoo
时,都会在该堆栈帧上分配此字符串文字的许多字节?如果是这样,不是吗?
const char FOO_NAME[] = "some_long_complicated_name";
bool isFoo(const char* bar) {
return !strcmp(bar, FOO_NAME);
}
更有效率吗?
答案 0 :(得分:7)
不,它们并非效率低下。通常将它们放置在已编译二进制文件的只读存储器部分中,因为它们的大小在编译时就已知,并且在运行时无法修改。
字符串的昂贵部分(就运行时性能而言)是内存分配。在isFoo
的两个版本中,都没有发生内存分配,因此我认为很难衡量两者之间的性能差异。 FOO_NAME
从技术上讲在某些地方占用了一些字节,但是很可能会被编译器优化掉。
Here都是编译器资源管理器上的两个版本。与-O3
的程序集并不相同,但是老实说,我无法进一步利用这些结果。
答案 1 :(得分:2)
常量字符串不会分配,它们仅存储在编译的二进制文件中,并可以通过指针进行访问。因此,没有,两种方法之间的速度没有差异。
答案 2 :(得分:1)
编译后的文件绝对没有变化。它将产生完全相同的二进制文件!
如果您在这样的单个可执行文件中编译两个版本:
bool isFoo(const char* bar) {
return !strcmp(bar, "some_long_complicated_name");
}
const char FOO_NAME[] = "some_long_complicated_name";
bool isFoo2(const char* bar) {
return !strcmp(bar, FOO_NAME);
}
int main()
{
isFoo( "nnn" );
isFoo2( "nnn" );
}
您可以研究二进制文件:
0000000000401156 <isFoo(char const*)>:
401156: 55 push %rbp
401157: 48 89 e5 mov %rsp,%rbp
40115a: 48 83 ec 10 sub $0x10,%rsp
40115e: 48 89 7d f8 mov %rdi,-0x8(%rbp)
401162: 48 8b 45 f8 mov -0x8(%rbp),%rax
401166: be c0 20 40 00 mov $0x4020c0,%esi
40116b: 48 89 c7 mov %rax,%rdi
40116e: e8 cd fe ff ff callq 401040 <strcmp@plt>
401173: 85 c0 test %eax,%eax
401175: 0f 94 c0 sete %al
401178: c9 leaveq
401179: c3 retq
000000000040117a <isFoo2(char const*)>:
40117a: 55 push %rbp
40117b: 48 89 e5 mov %rsp,%rbp
40117e: 48 83 ec 10 sub $0x10,%rsp
401182: 48 89 7d f8 mov %rdi,-0x8(%rbp)
401186: 48 8b 45 f8 mov -0x8(%rbp),%rax
40118a: be e0 20 40 00 mov $0x4020e0,%esi
40118f: 48 89 c7 mov %rax,%rdi
401192: e8 a9 fe ff ff callq 401040 <strcmp@plt>
401197: 85 c0 test %eax,%eax
401199: 0f 94 c0 sete %al
40119c: c9 leaveq
40119d: c3 retq
和字符串在这里:
4020c0 736f6d65 5f6c6f6e 675f636f 6d706c69 some_long_compli
4020d0 63617465 645f6e61 6d650000 00000000 cated_name......
4020e0 736f6d65 5f6c6f6e 675f636f 6d706c69 some_long_compli
4020f0 63617465 645f6e61 6d65006e 6e6e00 cated_name.nnn.
您还可以在此处看到“ nnn”字符串!
输出是通过以下方式生成的:
objdump -s -S go | c ++ filt> x
注意:您必须使用-O0
进行编译,否则编译器足够聪明,可以执行编译时已经存在的所有工作。如果我使用-O2
,则无法再看到任何字符串,并且所有调用结果都已存在于二进制文件中。很高兴看到编译器可以在编译时完成多少工作!
因此完全没有区别,完全相同的二进制代码。但是通过标准优化,编译时就不会生成用于字符串比较的代码!
我修改了main,以查看比较结果用于以下地方:
int main()
{
volatile bool x;
x = isFoo( "nnn" );
x = isFoo2( "nnn" );
}
生成的二进制文件:
0000000000401060 <main>:
}
int main()
{
volatile bool x;
x = isFoo( "nnn" );
401060: c6 44 24 ff 00 movb $0x0,-0x1(%rsp)
x = isFoo2( "nnn" );
}
401065: 31 c0 xor %eax,%eax
x = isFoo2( "nnn" );
401067: c6 44 24 ff 00 movb $0x0,-0x1(%rsp)
}
40106c: c3 retq
如您所见,比较结果已经存在于编译后的代码中。在运行时不再比较字符串。
有关速度和内存使用的所有问题:测量!如您在示例中看到的,结果与我们在其他答案中看到的大多数假设不同。如果速度或内存占用量确实很重要:查看编译器生成的结果。通常,它比您想的要完美得多!