在 Linux 上,我使用 g++ 和 clang++ 将以下代码编译成程序集。有关我如何做到这一点的更多信息,请参阅here。 (简短版:g++ -S -O3 filename.cppclang++ -S -O3 filename.cpp,还有一些您将在下面看到的组装 cmets 来帮助我。)
结论/TL;DR 在底部。
首先,我比较了label: 和goto 与do {} while。您不能将 for () {} 循环与 this 进行比较(出于善意),因为 for 循环总是首先评估条件。这一次,只有在循环代码执行一次后才会评估条件。
#include <iostream>
void testGoto()
{
__asm("//startTest");
int i = 0;
loop:
std::cout << i;
++i;
if (i < 100)
{
goto loop;
}
__asm("//endTest");
}
#include <iostream>
void testDoWhile()
{
__asm("//startTest");
int i = 0;
do
{
std::cout << i;
++i;
}
while (i < 100);
__asm("//endTest");
}
在这两种情况下,无论goto 或do {} while,每个编译器的程序集都是完全相同的:
g++:
xorl %ebx, %ebx
leaq _ZSt4cout(%rip), %rbp
.p2align 4,,10
.p2align 3
.L2:
movl %ebx, %esi
movq %rbp, %rdi
addl $1, %ebx
call _ZNSolsEi@PLT
cmpl $100, %ebx
jne .L2
叮当++:
xorl %ebx, %ebx
.p2align 4, 0x90
.LBB0_1: # =>This Inner Loop Header: Depth=1
movl $_ZSt4cout, %edi
movl %ebx, %esi
callq _ZNSolsEi
addl $1, %ebx
cmpl $100, %ebx
jne .LBB0_1
# %bb.2:
然后我比较了label: 和goto 与while {} 与for () {}。这一次,条件在循环代码执行一次之前就被评估了。
对于goto,我必须颠倒条件,至少是第一次。我看到了两种实现方式,所以我尝试了两种方式。
#include <iostream>
void testGoto1()
{
__asm("//startTest");
int i = 0;
loop:
if (i >= 100)
{
goto exitLoop;
}
std::cout << i;
++i;
goto loop;
exitLoop:
__asm("//endTest");
}
#include <iostream>
void testGoto2()
{
__asm("//startTest");
int i = 0;
if (i >= 100)
{
goto exitLoop;
}
loop:
std::cout << i;
++i;
if (i < 100)
{
goto loop;
}
exitLoop:
__asm("//endTest");
}
#include <iostream>
void testWhile()
{
__asm("//startTest");
int i = 0;
while (i < 100)
{
std::cout << i;
++i;
}
__asm("//endTest");
}
#include <iostream>
void testFor()
{
__asm("//startTest");
for (int i = 0; i < 100; ++i)
{
std::cout << i;
}
__asm("//endTest");
}
如上所述,在所有四种情况下,无论goto 1 还是2、while {} 或for () {},每个编译器的程序集都是完全相同的,g++ 只有1 个可能毫无意义的小例外:
g++:
xorl %ebx, %ebx
leaq _ZSt4cout(%rip), %rbp
.p2align 4,,10
.p2align 3
.L2:
movl %ebx, %esi
movq %rbp, %rdi
addl $1, %ebx
call _ZNSolsEi@PLT
cmpl $100, %ebx
jne .L2
g++ 的例外:在 goto2 程序集的末尾,程序集添加了:
.L3:
endbr64
(我认为这个额外的标签是从goto 1 的程序集中优化出来的。)不过我认为这完全无关紧要。
叮当++:
xorl %ebx, %ebx
.p2align 4, 0x90
.LBB0_1: # =>This Inner Loop Header: Depth=1
movl $_ZSt4cout, %edi
movl %ebx, %esi
callq _ZNSolsEi
addl $1, %ebx
cmpl $100, %ebx
jne .LBB0_1
# %bb.2:
结论/TL;DR:不,label: 和 goto、do {} while 的任何可能的等效安排之间似乎没有任何区别、while {} 和 for () {},至少在使用 g++ 9.3.0 和 clang++ 10.0.0 的 Linux 上。
注意我这里没有测试break和continue;但是,鉴于在任何情况下为 4 中的每一个生成的汇编代码都是相同的,我只能假设它们对于 break 和 continue 将完全相同,尤其是因为程序集使用标签和跳转每个场景。
为了确保正确的结果,我在过程中非常细致,还使用了 Visual Studio Code 的比较文件功能。