c语言排序代码case实现,C语言中switch case语句的实现

本文通过实例分析了C语言中switch-case与if-else在汇编层面的实现方式。当case数量较少时,二者通过比较实现跳转;而当case数量增加时,switch-case利用查询表提升效率。对于无规律的case分布,则无法生成查询表,转而采用if-else结构。通过对case顺序的调整,可以优化程序执行效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在程序中我们经常用到switch case,它的用法就不再用多说了。计算switch中的值然后比较,跳转到相应的分支。很多人说在编译是时通过转化成if。。else来实现的。但实际使用时尤其在调试代码时,每次的case跳转都是直接跳转到匹配值的。这样就与用if。。。else有些矛盾。那么switch。。case究竟如何实现的那?实际试验一下:

//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、付费专栏及课程。

余额充值