我最近读到有关标签的价值,
int main(){
int value = 2;
const void *labels[] = {&&val_0, &&val_1, &&val_2};
goto *labels[value];
val_0:
printf("The value is 0\n");
goto end;
val_1:
printf("The value is 1\n");
goto end;
val_2:
printf("The value is 2\n");
goto end;
end:
return(0);
}
我要问的是,使用此方法而不是使用switch语句或指针数组是否真的有任何性能提升?
答案 0 :(得分:5)
这是一个非标准的扩展,在这种情况下可能不会比同等的switch语句更好,并且应该避免恕我直言。 switch语句更清晰,更易于维护。
(我很快测试了我的gcc版本,它为这段代码和一个等效的switch语句产生了完全相同的代码。但它不是一个有代表性的测试,因为它优化了一切,除了实际选择的代码路径。)
一个潜在的性能考虑因素是,即使value
不在正确的范围内,switch语句也必须具有合理的行为,您的版本具有未定义的行为,因此编译器可能能够避免某些代码中的范围检查
答案 1 :(得分:3)
我认为switch语句更具可读性和清晰性,所以除非问题不适合switch语句,否则请使用它而不是数组。
标签作为值的最佳用途是在线程代码的解释器中:
解释器功能中的标签可以存储在 用于超快速调度的线程代码。
答案 2 :(得分:3)
答案在于gcc(-g
和-O2
)生成的汇编代码。这两个函数将value
作为参数,首先向用户询问(强制gcc不要删除未使用的代码片段 - 也就是死代码消除)。当然,printf
部分对于两个函数和gcc优化(两者)都是相同的,因此它在打印后立即返回。因此,重要的部分是两个功能的开始。我们来看看它们:
0x080484d0 <+0>: push %ebp # -
0x080484d1 <+1>: mov %esp,%ebp # |- standard prologue
0x080484d3 <+3>: sub $0x28,%esp # -
0x080484d6 <+6>: mov 0x8(%ebp),%eax # get argument
0x080484d9 <+9>: movl $0x80484f8,-0x14(%ebp) # set up labels array
0x080484e0 <+16>: movl $0x8048510,-0x10(%ebp)
0x080484e7 <+23>: movl $0x8048528,-0xc(%ebp)
0x080484ee <+30>: jmp *-0x14(%ebp,%eax,4) # jump to appropriate sect.
0x080484f2 <+34>: lea 0x0(%esi),%esi
0x08048470 <+0>: push %ebp # -
0x08048471 <+1>: mov %esp,%ebp # |- standard prologue
0x08048473 <+3>: sub $0x18,%esp # -
0x08048476 <+6>: mov 0x8(%ebp),%eax # get argument
0x08048479 <+9>: cmp $0x1,%eax
0x0804847c <+12>: je 0x80484b8 <switchFunc+72> # jump here if value == 1
0x0804847e <+14>: cmp $0x2,%eax
0x08048481 <+17>: je 0x80484a0 <switchFunc+48> # if value == 2
0x08048483 <+19>: test %eax,%eax
0x08048485 <+21>: jne 0x804849b <switchFunc+43> # if value != 0 return
两个片段都有一个“缓慢部分”:第一个片段花费大部分时间设置标签数组,而第二个片段慢速决定选择哪个路径。所以,基本上,他们的执行时间几乎相同
哪一个更好呢?第二个,具有switch
构造的那个。它是标准C,更易读,更易于维护和清晰。
答案 3 :(得分:2)
然而,switch
的有效汇编仍然是一个悬而未决的问题,例如, this paper
虽然标签为值和间接goto
- s确实是GCC语言扩展,但它已被其他编译器(icc
,LLVM / clang
)采用,并且确实有用(并且是一个低级功能,本着C的精神),特别是对于编码线程代码解释器,自动机等等....我不知道为什么这个扩展没有成为标准(我猜它没有标准化,因为社会或经济因素,而不是技术上的无用。)。
答案 4 :(得分:0)
以下是gnu gcc手册中有关此功能的文档 http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html