C语言拾遗(五):分析switch语句机制---下篇
2013-04-19 22:25
417 查看
想要深入地理解语言的运行机理,阅读汇编代码是很有帮助的。
前奏:我们这里用的汇编代码格式是AT&T的,这个微软的intel格式不一样。
AT&T格式是GCC,OBJDUMP等一些其他我们在linux环境下常用工具的默认格式。
今天就一起再来看看switch语句吧。
关键词:跳转,跳转表
先来一个最简单的例子:
看一下其汇编代码:我会逐条注释。
命令是gcc -O1 -S test2.c
好了,对着注释看,是不是很简单呢。由此我们知道,switch语句实际上也是一种条件语句,而条件语句的核心是跳转。聪明的你应该还会想到跳转的标签个数应该是和case语句的分支个数成正比的。
可是,当case语句分支很多时,岂不是各种jmp?编译器很聪明的使用了一种叫跳转表的方法来解决这个问题。
其实也简单,跳转表的思想就是将要跳转的代码的地址存入一个数组中,然后根据不同的条件跳转到对应的地址处,就像访问数组一样。
空说太枯燥了,还是看个例子吧。(例子来源:深入理解计算机系统3.6.7)
C:
同样的看一下对应的汇编。我省略了一些无关的代码。
解释一下关键点:
首先生成了一张跳转表,以L7为基准,4自己为对齐单位,加上偏移就能跳转到相应的标签。
比如,L7+0就是跳到L3处,L7+4就是跳转到L8处,依次类推。
第6行: jmp *.L7(,%edx,4)
表示 goto *jt[index],举个例子,假设现在n是102,edx里面是2(102-100),查表得L7+2*4处,即跳到L4处。
将eax的值+10,这和C是对应的。
好了,其他的分支,各位可以自己用其他例子验证一下,看是不是跟C语言代码逻辑是一样的,欢迎讨论。
小结:
swith语句的本质是条件语句,条件语句的本质是跳转。
当case分支多了的时候(一般大于四个时),编译器巧妙地通过跳转表来访问代码位置。
---End---
前奏:我们这里用的汇编代码格式是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---
相关文章推荐
- C语言拾遗(五):分析switch语句机制
- C语言拾遗(四):分析switch语句机制---上篇
- C语言拾遗(四):分析switch语句机制
- C语言 switch语句的使用总结
- 快速识别汇编中等价的C语言语句(if, while, for, switch)
- 逆向随笔 - switch 语句深入分析
- C语言 if,switch,do,while,for分析
- Android内存机制分析下篇:分析APP内存使用情况
- C语言switch语句
- Open vSwitch(OvS)源代码之Linux RCU锁机制分析
- 由一个switch语句理解c语言中getchar函数
- C语言的switch语句跳转问题
- Android内存机制分析下篇:分析APP内存使用情况
- struts2源码分析-IOC容器的实现机制(下篇)
- C语言之switch case语句 case 中没有break时会继续执行下一个case无论条件是否满足都会执行
- C语言利用switch语句实现输入一个字母,输出它前后紧挨着的字母
- android library工程中使用switch_case语句,调用资源文件的id出现错误的原因分析及解决方法
- c语言中用数组代替switch语句投掷骰子
- C语言中switch语句的思考
- C语言第六篇:用switch语句实现四则运算器