您的位置:首页 > 编程语言 > C语言/C++

C语言拾遗(五):分析switch语句机制---下篇

2013-04-19 22:25 417 查看
想要深入地理解语言的运行机理,阅读汇编代码是很有帮助的。

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

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

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

关键词:跳转,跳转表

先来一个最简单的例子:

int switch_eg(int x, int n)
{
int result = x;

switch (n) {
case 100:
result += 10;
break;
case 102:
result -= 10;
break;
default:
result = 0;
}

return result;
}


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

命令是gcc -O1 -S test2.c

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


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

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

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

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

C:

int switch_eg(int x, int n) {
int result = x;
switch (n) {
case 100:
result *= 13;
break;

case 102:
result += 10;
/* fall throuth */

case 103:
result += 11;
break;

case 104:
case 106:
result *= result;
break;

default:
result = 0;
}

return result;
}


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

movl    4(%esp), %eax
movl    8(%esp), %edx
subl    $100, %edx
cmpl    $6, %edx
ja    .L8
jmp    *.L7(,%edx,4)
.section    .rodata
.align 4
.align 4
.L7:
.long    .L3
.long    .L8
.long    .L4
.long    .L5
.long    .L6
.long    .L8
.long    .L6
.text
.L3:
leal    (%eax,%eax,2), %edx
leal    (%eax,%edx,4), %eax
ret
.L4:
addl    $10, %eax
.L5:
addl    $11, %eax
ret
.L6:
imull    %eax, %eax
ret
.L8:
movl    $0, %eax
ret


解释一下关键点:

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

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


.section    .rodata
.align 4
.align 4
10 .L7:
.long    .L3
.long    .L8
.long    .L4
.long    .L5
.long    .L6
.long    .L8
.long    .L6

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

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

.L4:
addl    $10, %eax


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

case 102:
result += 10;

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

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

小结:

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

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

---End---
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: