zoukankan      html  css  js  c++  java
  • C语言拾遗(五):分析switch语句机制下篇

    想要深入地理解语言的运行机理,阅读汇编代码是很有帮助的。

    前奏:我们这里用的汇编代码格式是AT&T的,这个微软的intel格式不一样。

    AT&T格式是GCC,OBJDUMP等一些其他我们在linux环境下常用工具的默认格式。

    今天就一起再来看看switch语句吧。

    关键词:跳转,跳转表

    先来一个最简单的例子:

     1 int switch_eg(int x, int n)
     2 {
     3     int result = x;
     4 
     5     switch (n) {
     6         case 100:
     7             result += 10;
     8             break;
     9         case 102:
    10              result -= 10;
    11             break;
    12         default:
    13             result = 0;        
    14     }
    15     
    16     return result;
    17 }

    看一下其汇编代码:我会逐条注释。

    命令是gcc -O1 -S test2.c

     1     .file    "test2.c"
     2     .text
     3     .globl    switch_eg
     4     .type    switch_eg, @function
     5 switch_eg:
     6 .LFB0:
     7     .cfi_startproc
     8     movl    4(%esp), %ecx                  //x在esp+4的位置,存入寄存器ecx
     9     movl    8(%esp), %edx                  //n在esp+8的位置,存入寄存器edx
    10     leal    10(%ecx), %eax                 //将ecx的值+10存入eax,也就是x+10
    11     cmpl    $100, %edx                     //将n和100比较
    12     je    .L2                              //n==100,跳到L2
    13     subl    $10, %ecx                      //n!=100,x=x-10
    14     cmpl    $102, %edx                     //n和102比较
    15     movl    $0, %eax                       //eax=0
    16     cmove    %ecx, %eax                    //如果n==102,eax=ecx
    17 .L2:
    18     rep                                    //返回,result存在eax
    19     ret
    20     .cfi_endproc
    21 .LFE0:
    22     .size    switch_eg, .-switch_eg
    23     .ident    "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    24     .section    .note.GNU-stack,"",@progbits

    好了,对着注释看,是不是很简单呢。由此我们知道,switch语句实际上也是一种条件语句,而条件语句的核心是跳转。聪明的你应该还会想到跳转的标签个数应该是和case语句的分支个数成正比的。

    可是,当case语句分支很多时,岂不是各种jmp?编译器很聪明的使用了一种叫跳转表的方法来解决这个问题。

    其实也简单,跳转表的思想就是将要跳转的代码的地址存入一个数组中,然后根据不同的条件跳转到对应的地址处,就像访问数组一样。

    空说太枯燥了,还是看个例子吧。(例子来源:深入理解计算机系统3.6.7)

    C:

     1 int switch_eg(int x, int n) {
     2     int result = x;
     3     switch (n) {
     4         case 100:
     5             result *= 13;
     6             break;
     7 
     8         case 102:
     9             result += 10;
    10             /* fall throuth */
    11         
    12         case 103:
    13             result += 11;
    14             break;
    15 
    16         case 104:
    17         case 106:
    18             result *= result;
    19             break;
    20 
    21         default:
    22             result = 0;
    23     }
    24 
    25     return result;
    26 }

    同样的看一下对应的汇编。我省略了一些无关的代码。

     1     movl    4(%esp), %eax
     2     movl    8(%esp), %edx
     3     subl    $100, %edx
     4     cmpl    $6, %edx
     5     ja    .L8
     6     jmp    *.L7(,%edx,4)
     7     .section    .rodata
     8     .align 4
     9     .align 4
    10 .L7:
    11     .long    .L3
    12     .long    .L8
    13     .long    .L4
    14     .long    .L5
    15     .long    .L6
    16     .long    .L8
    17     .long    .L6
    18     .text
    19 .L3:
    20     leal    (%eax,%eax,2), %edx
    21     leal    (%eax,%edx,4), %eax
    22     ret
    23 .L4:
    24     addl    $10, %eax
    25 .L5:
    26     addl    $11, %eax
    27     ret
    28 .L6:
    29     imull    %eax, %eax
    30     ret
    31 .L8:
    32     movl    $0, %eax
    33     ret

    解释一下关键点:

    首先生成了一张跳转表,以L7为基准,4自己为对齐单位,加上偏移就能跳转到相应的标签。

    比如,L7+0就是跳到L3处,L7+4就是跳转到L8处,依次类推。

    
    
     7     .section    .rodata
     8     .align 4
     9     .align 4
    10
    .L7: 11 .long .L3 12 .long .L8 13 .long .L4 14 .long .L5 15 .long .L6 16 .long .L8 17 .long .L6

    第6行: jmp *.L7(,%edx,4)

    表示   goto *jt[index],举个例子,假设现在n是102,edx里面是2(102-100),查表得L7+2*4处,即跳到L4处。

    23 .L4:
    24     addl    $10, %eax

    将eax的值+10,这和C是对应的。

     8         case 102:
     9             result += 10;

    注意到L4后面没有ret了,这就是我们上篇所说的fall through规则。不清楚可以看一下上篇的例子C语言拾遗(四):分析switch语句机制---上篇

     好了,其他的分支,各位可以自己用其他例子验证一下,看是不是跟C语言代码逻辑是一样的,欢迎讨论。

    小结:

    swith语句的本质是条件语句,条件语句的本质是跳转。

    当case分支多了的时候(一般大于四个时),编译器巧妙地通过跳转表来访问代码位置。

    ---End---

  • 相关阅读:
    hdu:2222:Keywords Search(AC自动机模板题)
    3400: [Usaco2009 Mar]Cow Frisbee Team 奶牛沙盘队
    bzoj:1026: [SCOI2009]windy数(数位dp)
    poj:1985:Cow Marathon(求树的直径)
    RQNOJ:PID30 / [stupid]愚蠢的矿工☆(树形背包)
    002 Android 系统特有的类介绍及使用
    001 Android TextUtils工具类的使用
    java 常用模块代码
    045 Android Studio 常用应用
    Android Studio 常用技巧
  • 原文地址:https://www.cnblogs.com/RandyXu/p/3031643.html
Copyright © 2011-2022 走看看