1.switch...case结构的汇编表示
写入switch...case结构的代码:
int fun(char c)
{
char res;
switch(c)
{
case 'a':
res='a';
break;
case 'e':
res='e';
break;
case 'i':
res='i';
break;
case 'o':
res='o';
break;
case 'u':
res='u';
break;
default:
res=' ';
}
用gcc产生的汇编代码是:
00000000 <fun>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 14 sub $0x14,%esp
6: 8b 45 08 mov 0x8(%ebp),%eax
9: 88 45 ec mov %al,-0x14(%ebp)
c: 0f be 45 ec movsbl -0x14(%ebp),%eax
10: 83 e8 61 sub $0x61,%eax
13: 83 f8 14 cmp $0x14,%eax
16: 77 27 ja 3f <fun+0x3f>
18: 8b 04 85 00 00 00 00 mov 0x0(,%eax,4),%eax
1f: ff e0 jmp *%eax
21: c6 45 ff 61 movb $0x61,-0x1(%ebp)
25: eb 1c jmp 43 <fun+0x43>
27: c6 45 ff 65 movb $0x65,-0x1(%ebp)
2b: eb 16 jmp 43 <fun+0x43>
2d: c6 45 ff 69 movb $0x69,-0x1(%ebp)
31: eb 10 jmp 43 <fun+0x43>
33: c6 45 ff 6f movb $0x6f,-0x1(%ebp)
37: eb 0a jmp 43 <fun+0x43>
39: c6 45 ff 75 movb $0x75,-0x1(%ebp)
3d: eb 04 jmp 43 <fun+0x43>
3f: c6 45 ff 20 movb $0x20,-0x1(%ebp)
43: 0f be 45 ff movsbl -0x1(%ebp),%eax
47: c9 leave
48: c3 ret
计算机中处理switch...case结构时,会生成跳转表,根据变量的取值跳转到合适的分支。在上面的例子中,变量c存放在ebp偏移8字节的位置,它首先减去97,如果大于20则跳转到默认分支。在执行完对应的分支后,每个分支后都有jmp43语句用于跳转到函数的结尾。
2.if...else语句的汇编表示
上面的代码写成if...else的形式,产生的汇编代码是:
00000000 <fun>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 14 sub $0x14,%esp
6: 8b 45 08 mov 0x8(%ebp),%eax
9: 88 45 ec mov %al,-0x14(%ebp)
c: 80 7d ec 61 cmpb $0x61,-0x14(%ebp)
10: 75 06 jne 18 <fun+0x18>
12: c6 45 ff 61 movb $0x61,-0x1(%ebp)
16: eb 34 jmp 4c <fun+0x4c>
18: 80 7d ec 65 cmpb $0x65,-0x14(%ebp)
1c: 75 06 jne 24 <fun+0x24>
1e: c6 45 ff 65 movb $0x65,-0x1(%ebp)
22: eb 28 jmp 4c <fun+0x4c>
24: 80 7d ec 69 cmpb $0x69,-0x14(%ebp)
28: 75 06 jne 30 <fun+0x30>
2a: c6 45 ff 69 movb $0x69,-0x1(%ebp)
2e: eb 1c jmp 4c <fun+0x4c>
30: 80 7d ec 6f cmpb $0x6f,-0x14(%ebp)
34: 75 06 jne 3c <fun+0x3c>
36: c6 45 ff 6f movb $0x6f,-0x1(%ebp)
3a: eb 10 jmp 4c <fun+0x4c>
3c: 80 7d ec 75 cmpb $0x75,-0x14(%ebp)
40: 75 06 jne 48 <fun+0x48>
42: c6 45 ff 75 movb $0x75,-0x1(%ebp)
46: eb 04 jmp 4c <fun+0x4c>
48: c6 45 ff 20 movb $0x20,-0x1(%ebp)
4c: 0f be 45 ff movsbl -0x1(%ebp),%eax
50: c9 leave
51: c3 ret
可见,如果输入了字符a,这个程序将不需要跳转,这时if...else的效率要高于switch...case.如果是输入了字符o或者u,程序将多次跳转,程序的分支变多时,必然会导致效率降低。
3.switch...case语句和if...else效率比较
这里不考虑编译器的优化和条件传送(条件传送介绍),仅仅从跳转次数来比较两者的效率。
switch...case结构中有跳转表,输入的字符只要经过一次比较就可以正确的找到跳转分支,所以平均情况下跳转次数为1.
if...else结构如果有n个分支,分别记为n0,n1,n2,n3,...n(i-1),每个分支出现的概率假设未pi,分别为p0,p1,p2,p3,...p(i-1)。执行第一分支前不需要跳转,其它分支均需要跳转,执行第二个分支之前需要跳转一次,第三个分支需要跳转两次...需要跳转平均跳转的次数s为:
如果
>1,则此时if...case的效率是小于switch...case的,如果它的值小于1,此时if...else的效率高于switch...case。 假设if...else分支的每个分支出现的概率相同,即1/n,上面的跳转次数的期望值可以改写为:
。
如果n=3,则期望的跳转次数为1,刚好与switch...case相同。由此,如果选择分支大于3的时候,选用switch...case结构效率会更高一些,而小于3时,选用if...else结构更好。
总结:在选择分支较多时,选用switch...case结构会提高程序的效率,但switch不足的地方在于只能处理字符或者数字类型的变量,if...else结构更加灵活一些,if...else结构可以用于判断表达式是否成立,比如if(a+b>c),if...else的应用范围更广,switch...case结构在某些情况下可以替代if...else结构。