C语言switch的机制,C语言拾遗(五):分析switch语句机制

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

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

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

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

关键词:跳转,跳转表

先来一个最简单的例子:

1 int switch_eg(int x, intn)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 returnresult;17 }

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

命令是gcc -O1 -S test2.c

1 .file "test2.c"

2 .text3 .globl switch_eg4 .type switch_eg, @function5 switch_eg:

6 .LFB0:

7 .cfi_startproc8 movl 4(%esp), %ecx //x在esp+4的位置,存入寄存器ecx9 movl 8(%esp), %edx //n在esp+8的位置,存入寄存器edx10 leal 10(%ecx), %eax //将ecx的值+10存入eax,也就是x+1011 cmpl $100, %edx //将n和100比较12 je.L2 //n==100,跳到L213 subl $10, %ecx //n!=100,x=x-1014 cmpl $102, %edx //n和102比较15 movl $0, %eax //eax=016 cmove%ecx, %eax //如果n==102,eax=ecx17 .L2:

18 rep //返回,result存在eax

19 ret

20 .cfi_endproc21 .LFE0:

22 .size switch_eg, .-switch_eg23 .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, intn) {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 returnresult;26 }

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

1 movl 4(%esp), %eax2 movl 8(%esp), %edx3 subl $100, %edx4 cmpl $6, %edx5 ja.L86 jmp *.L7(,%edx,4)7 .section .rodata8 .align 4

9 .align 4

10 .L7:

11 .long .L312 .long .L813 .long .L414 .long .L515 .long .L616 .long .L817 .long .L618 .text19 .L3:

20 leal (%eax,%eax,2), %edx21 leal (%eax,%edx,4), %eax22 ret

23 .L4:

24 addl $10, %eax25 .L5:

26 addl $11, %eax27 ret

28 .L6:

29 imull %eax, %eax30 ret

31 .L8:

32 movl $0, %eax33 ret

解释一下关键点:

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

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

7 .section .rodata8 .align 4

9 .align 410 .L7:

11 .long .L312 .long .L813 .long .L414 .long .L515 .long .L616 .long .L817 .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---

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值