标签作为值与switch语句

时间:2011-11-05 11:20:45

标签: c gcc

我最近读到有关标签的价值,

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语句或指针数组是否真的有任何性能提升?

5 个答案:

答案 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