c语言 switch case 比较,C语言中switch case语句的实现(switch case 和 else if 的比较、区别)...

本文通过对C语言switch-case和if-else结构的代码进行编译到汇编的转换,探讨了两种结构在实现上的差异。当case数量较少时,二者实现方式相似,通过比较和跳转实现。但随着case数量增加,编译器可能会生成查询表以提高效率。当case值分布无规律时,无法生成查询表,将退化为if-else结构。该分析揭示了编译器在优化代码时的空间和时间权衡策略。
摘要由CSDN通过智能技术生成

//switch_test1.c

#include

#include

int main()

{

int i;

i=1;

switch(i)

{

case 1:

printf("%d/n",i);

break;

case 2:

printf("%d/n",i);

break;

case 3:

printf("%d/n",i);

break;

case 4:

printf("%d/n",i);

break;

deflaut:

break;

}

return 1;

}

对test1.c用Gcc进行到汇编程序的编译

Gcc  -S test1.c –o test.s

得到文件内容如下:

.file "switch-test1.c"

.def ___main; .scl 2; .type 32; .endef

.section .rdata,"dr"

LC0:

.ascii "%d/12/0"

.text

.globl _main

.def _main; .scl 2; .type 32; .endef

_main:

pushl %ebp

movl %esp, %ebp

subl $24, %esp

andl $-16, %esp

movl $0, %eax

addl $15, %eax

addl $15, %eax

shrl $4, %eax

sall $4, %eax

movl %eax, -12(%ebp)

movl -12(%ebp), %eax

call __alloca

call ___main

movl $1, -4(%ebp)

movl -4(%ebp), %eax

movl %eax, -8(%ebp)

cmpl $2, -8(%ebp)

je L4

cmpl $2, -8(%ebp)

jg L8

cmpl $1, -8(%ebp)

je L3

jmp L2

L8:

cmpl $3, -8(%ebp)

je L5

cmpl $4, -8(%ebp)

je L6

jmp L2

L3:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L2

L4:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L2

L5:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L2

L6:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

L7:

L2:

movl $1, %eax

leave

ret

.def _printf; .scl 3; .type 32; .endef

Linux的汇编格式与Intel的不同,但是基本指令还是部分相似的,在对操作数取用时会加%,而且源和宿与intel相反。具体的内容不讨论。L3到L6程序片段内容基本相似,是调用printf函数实现打印。L8及之前可以看出通过大量的CMPL比较指令进行比较i与立即数比较,相等则实现跳转。查看部分程序:

cmpl $2, -8(%ebp)#低八位与2比较

je L4#相等则跳转L4,打印i

cmpl $2, -8(%ebp) #低八位与2比较

jg L8//大于则跳转L8

cmpl $1, -8(%ebp) #低八位与1比较

je L3#相等则跳转L3

jmp L2#跳转到L2,返回1结束程序

L8:

cmpl $3, -8(%ebp) #低八位与3比较

je L5#相等则跳转L6,打印i

cmpl $4, -8(%ebp) #低八位与4比较

je L6#相等则跳转L6,打印i

jmp L2

经过注释可以看出,所有的case想都经过比较来实现是跳转的。再看一下if-else的实现会如何,

//if_else.c

#include

#include

int main()

{

int i;

i=1;

if(i==1)

{

printf("%d/n",i);

}

else if(i==2)

{

printf("%d/n",i);

}

else if(i==3)

{

printf("%d/n",i);

}

else if(i==4)

{

printf("%d/n",i);

}

else

{

}

return 1;

}

同样编译至汇编,查看生成的汇编代码:

.file "if_else.c"

.def ___main; .scl 2; .type 32; .endef

.section .rdata,"dr"

LC0:

.ascii "%d/12/0"

.text

.globl _main

.def _main; .scl 2; .type 32; .endef

_main:

pushl %ebp

movl %esp, %ebp

subl $24, %esp

andl $-16, %esp

movl $0, %eax

addl $15, %eax

addl $15, %eax

shrl $4, %eax

sall $4, %eax

movl %eax, -8(%ebp)

movl -8(%ebp), %eax

call __alloca

call ___main

movl $1, -4(%ebp)

cmpl $1, -4(%ebp)

jne L2

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L3

L2:

cmpl $2, -4(%ebp)

jne L4

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L3

L4:

cmpl $3, -4(%ebp)

jne L6

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L3

L6:

cmpl $4, -4(%ebp)

jne L3

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

L3:

movl $1, %eax

leave

ret

.def _printf; .scl 3; .type 32; .endef

简单阅读就可以发现,两者的程序有点差距,但是基本实现逻辑都是相似,比较i与立即数(c程序中的常数1,2,3,4),相等时打印,然后跳转到返回结束程序。好像真的是如大家所说,switch-case程序最终实现会使用if-else的逻辑实现。但是为什么,在调试会有直接跳转的问题那,实际程序与例程的区别应该就在,case数上了。实际是case分支高达20多个,那么提升case分支再次尝试一下。

//switch_test2.c

#include

#include

int main()

{

int i;

i=1;

switch(i)

{

case 1:

printf("%d/n",i);

break;

case 2:

printf("%d/n",i);

break;

case 3:

printf("%d/n",i);

break;

case 4:

printf("%d/n",i);

break;

case 5:

printf("%d/n",i);

break;

case 6:

printf("%d/n",i);

break;

case 7:

printf("%d/n",i);

break;

case 8:

printf("%d/n",i);

break;

deflaut:

break;

}

return 1;

}

汇编代码:

.file "switch_test2.c"

.def ___main; .scl 2; .type 32; .endef

.section .rdata,"dr"

LC0:

.ascii "%d/12/0"

.text

.globl _main

.def _main; .scl 2; .type 32; .endef

_main:

pushl %ebp

movl %esp, %ebp

subl $24, %esp

andl $-16, %esp

movl $0, %eax

addl $15, %eax

addl $15, %eax

shrl $4, %eax

sall $4, %eax

movl %eax, -8(%ebp)

movl -8(%ebp), %eax

call __alloca

call ___main

movl $1, -4(%ebp)

cmpl $8, -4(%ebp)

ja L2

movl -4(%ebp), %eax

sall $2, %eax

movl L12(%eax), %eax

jmp *%eax

.section .rdata,"dr"

.align 4

L12:

.long L2

.long L3

.long L4

.long L5

.long L6

.long L7

.long L8

.long L9

.long L10

.text

L3:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L2

L4:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L2

L5:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L2

L6:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L2

L7:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L2

L8:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L2

L9:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

jmp L2

L10:

movl -4(%ebp), %eax

movl %eax, 4(%esp)

movl $LC0, (%esp)

call _printf

L11:

L2:

movl $1, %eax

leave

ret

.def _printf; .scl 3; .type 32; .endef

简单的分析可以看出L12作为一个表项,实现了到L2到L10的跳转。那么可以看出,switch-case在实现时当case项比较多时,会通过生成查询表来提高程序的效率,空间换时间。

在此基础上,对case的顺序打乱,只要case的值比较规律(数据差相同),都是汇编成查询表,但是各个case值之间非常离散时,即无规律可言时,是不能能生成查询表的,只能使用if-else的方式。这种情况下只用将最有可能的值放在第一个比较判断的位置才能极大提高程序效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值