switch语句特别有用,这种语句不仅提高了C代码的可读行, 而且通过使用一种成为跳转表(jump table)的数据结构使得实现更加高效。
跳转表是一个数组,表项i是一个代码段的地址。程序代码用switch索引值来执行一个跳转表内的数据引用,确认跳转指令的目的。
gcc编译器根据case的数量与case值的稀疏程度来翻译开关语句。 当case的数量比较多,并且case值稀疏比较小时, 才会使用跳转表。 否则使用普通跳转,相当与if-else。
switch_one.c:
/* not use jump table */
int switch_one(int x)
{
int result = x;
switch (x) {
case 1:
case 2:
case 3:
result += 10;
break;
case 4:
case 5:
case 6:
result += 40;
break;
default:
result = 0;
}
return result;
}
switch_two.c:
/* use jump table */
int switch_two(int x)
{
int result = x;
switch (x) {
case 1:
result += 10;
break;
case 2:
result += 10;
break;
case 3:
result += 10;
break;
case 4:
result += 20;
break;
case 5:
result += 20;
break;
case 6:
result += 20;
break;
default:
result = 0;
}
return result;
}
使用gcc进行编译,编译时通过加-S选项, 产生汇编代码:
gcc -O2 -S switch_one.c产生switch_one.s, 用文本编辑器查看:
.file "switch_one.c"
.text
.p2align 4,,15
.globl _switch_one
.def _switch_one; .scl 2; .type 32; .endef
_switch_one:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
cmpl $1, %edx
movl %edx, %eax
jl L9
cmpl $3, %edx
jle L5
cmpl $6, %edx
jg L9
popl %ebp
leal 20(%edx), %eax
ret
.p2align 4,,7
L5:
popl %ebp
addl $10, %eax
ret
.p2align 4,,7
L9:
popl %ebp
xorl %eax, %eax
ret
gcc -O2 -S switch_two.c产生switch_two.s, 用文本编辑器查看:
.file "switch_two.c"
.text
.p2align 4,,15
.globl _switch_two
.def _switch_two; .scl 2; .type 32; .endef
_switch_two:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx
cmpl $6, %edx
ja L9
jmp *L10(,%edx,4)
.section .rdata,"dr"
.align 4
L10:
.long L9
.long L5
.long L5
.long L5
.long L8
.long L8
.long L8
.text
.p2align 4,,7
L9:
popl %ebp
xorl %eax, %eax
ret
.p2align 4,,7
L8:
popl %ebp
leal 20(%edx), %eax
ret
.p2align 4,,7
L5:
popl %ebp
leal 10(%edx), %eax
ret
观察:
swithc_two.s中的L10, L10是个地址数组。
小结:
switch_one未使用跳转表。
switch_one与switch_two的case数量相同, 因为switch_two的case值稀疏相对比较小,所以switch_two使用了跳转表。