我的代码看起来像这样(显示了完成的所有用法):
bool done = false;
for(int i = 0; i < big; i++)
{
...
for(int j = 0; j < wow; j++)
{
...
if(foo(i,j))
{
done = true;
break;
}
...
}
if(done) break;
...
}
任何编译器都会将其转换为这样:
for(int i = 0; i < big; i++)
{
...
for(int j = 0; j < wow; j++)
{
...
if(foo(i,j))
goto __done; // same as a labeled break if we had it
...
}
...
}
__done:;
注意: 虽然我最感兴趣的是
if(done)break;
是否被绕过并作为死代码删除,但我也对它和 done
是否被完全删除感兴趣。
显然这取决于编译器。当您不确定时,最好的办法是查看编译器的汇编输出(所有流行的编译器都有一个开关)。即使您不熟悉汇编,您至少可以将调试版本与优化版本进行比较。
话虽这么说,这是
goto
不是一个坏主意的少数情况之一。请随意使用它来打破内部循环。
编辑
刚刚在 VS2010 中尝试了以下方法,它确实优化了外部条件:
bool done = false;
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 10; j++)
{
if(i == 7 && j == 3)
{
done = true;
break;
}
}
if(done) break;
}
return 0;
GNU 编译器就是这样做的,从优化级别 -O1 开始(我在 x86_64 上使用 gcc 4.5.1)
call _Z3fooii // al = foo(i,j)
testb %al, %al
jne .L14
...
其中 .L14 是放置在您放置 __done 的位置的标签:
更好的问题可能是:哪个现代编译器不执行此优化?
我并不是想刻薄,但是……这有关系吗? 一般来说,我认为最好让编译器完成他们的工作,而该工作就是根据源代码生成“最佳”(请注意,“最佳”可能会根据您的需求而变化)编译后的代码。 代码中的任何性能考虑因素都应该通过分析器和算法复杂性的良好工作知识来确定。
如果您只是好奇,请忽略此评论。 但如果您的目的是以某种方式优化您的代码,那么我认为还有更好的途径。
我已经尝试过 GCC 4.2.1:
// Prevent optimizing out calls to foo and loop unrolling:
extern int big, wow;
bool foo(int,int);
void
bar()
{
int done = false;
for(int i = 0; i < big; i++)
{
for(int j = 0; j < wow; j++)
{
if(foo(i,j))
{
done = true;
break;
}
}
if(done)
break;
}
}
...它直接进入后序部分
-O3
:
33: e8 fc ff ff ff call 34 <bar()+0x34> ; call to foo*
38: 84 c0 test %al,%al
3a: 74 e5 je 21 <bar()+0x21> ; next loop iteration
3c: 83 c4 10 add $0x10,%esp
3f: 5b pop %ebx
40: 5e pop %esi
41: 5d pop %ebp
42: c3 ret
*** 这是来自未链接的目标文件,
call 34
实际上是对foo
的调用。