switch--case的执行顺序

fohonet 2007-04-10 11:35:44
有 这么一段程序
请问case如何判断执行到Type?

switch(Type)
{
case 2x2:
case 3x3:
case 4x4:
case 5x5:
EnterType = Type;//是怎么判断执行到EnterType = Type?
break;
default:
EnterType = 2x2;
}
...全文
1472 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
Siela 2007-04-16
  • 打赏
  • 举报
回复
学习中
猪小烧 2007-04-16
  • 打赏
  • 举报
回复
华山论剑。。。
wntdhouyan 2007-04-13
  • 打赏
  • 举报
回复
从上到下。一直到break;为止
vc_asm 2007-04-13
  • 打赏
  • 举报
回复
所以lz的asm代码中少了比较命中值的部分,容易让人误会
vc_asm 2007-04-13
  • 打赏
  • 举报
回复
先把可能命中的值放在一个列表中,然后把相应代码入口放在另一张表中,把待选值在命中表中循环比较,发现命中,就根据索引从入口表中检索出地址,跳转过去,非常的高效
fscd 2007-04-11
  • 打赏
  • 举报
回复
但问题是 case的数据是相差很远或无规律时,关联表跳转是不太可能。
比如换成如下:
case 1 :
res = 1;
break ;
case 13 :
res = 2;
break ;
case 107 :
res = 2;
break ;
case 1379:
res = 3 ;
break ;
case 13579:
res = 4 ;
break ;
case 135791:
res = 5 ;
break ;
case 1357913:
res = 6 ;
break ;

这样还能用关联表找到偏移入口吗?
xuancaoer 2007-04-11
  • 打赏
  • 举报
回复
mark
fohonet 2007-04-11
  • 打赏
  • 举报
回复
为什么 case 语句在不加break的时候具有直通性?
这是因为跳转是在进入 switch 是计算出的,而不是在case语句中计算出的,整个
case 语句群就是一块完整而连续的代码,只是switch让其从不同的位置开始执行。

========================

这句很很有启发。






JackLucifer 2007-04-11
  • 打赏
  • 举报
回复
lovesnow1314(流浪),长见识了。
gogowhy 2007-04-11
  • 打赏
  • 举报
回复
m
山中岁月 2007-04-11
  • 打赏
  • 举报
回复
高人,学习了
KennyLiu 2007-04-10
  • 打赏
  • 举报
回复

精彩~~
lovesnow1314 2007-04-10
  • 打赏
  • 举报
回复
仔细比较一下这个最新的 new_ta.s 与前面的 ta.s,精华全在里面了!
首先 new_ta.s 比前面的 ta.s 多了一个 .L9 部分,而且它的 // 1 ~ // 7 中没有了前面
ta.s 文件中所存在的众多的 cmpl 与 jmp 语句,那么,现在这样的代码又是怎么实现
switch 语句中的跳转的呢?我们来仔细分析一下它新多出来的 .L9 部份。
.section .rodata
.align 4
.align 4
.L9:
.long .L3
.long .L2
.long .L4
.long .L5
.long .L6
.long .L7
.long .L8
.text
显而易见,.L9 部份是一个我们最常见的数据结构——表,它的每一项都是一个标号,而这个标号,恰恰是每个 case 语句的入口标号!
这很容易让我们想到,它很可能是用了一张表来存放所有的 case 语句的入口,然后,在
执行 switch 语句的时候就从这个表中直接检出相应的 case 语句的入口地址,然后跳转
到相应的 case 语句去执行,就像hash_table似的。具体是不是这样呢?我们看看进入
switch 部份的代码:

pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
subl $100, %eax
movl %eax, -8(%ebp)
cmpl $6, -8(%ebp)
ja .L2
movl -8(%ebp), %edx
movl .L9(,%edx,4), %eax // 1
jmp *%eax // 2
果然如此!首先在 // 1 处根据%edp的值(其值相当于表的下标)在.L9的表中找到相应
case 语句的入口地址,并把这个地址存到%eax中,然后通过 // 2 (这是一个间接跳转
语句)转到%eax存放的地址中,也即相应的case语句处。
C编译器,果然聪明!

通过这个分析我们可以知道如下两点:
1. 当 case 语句少的时候,C编译器将其转成 if..else.. 类型进行处理,运用较多的
cmp 与 jmp 语句 ,而当 case 语句较多的时候,C编译器会出成一个跳转表,而直
接通过跳转表进行跳转,这让 switch 具有非常高的效律,而且效律几乎不会因为
case 语句的增长而减小,李维所担忧的问题是完全不会发生的
2. 可以问答下面几个问题:
1. 为什么 case 语句中需要的是整数类型而不能是其余的类型?
这是因为,case 语句中的这个值是用来做跳转表的下标的,因此,当然必须是整数
2. 为什么 case 语句在不加break的时候具有直通性?
这是因为跳转是在进入 switch 是计算出的,而不是在case语句中计算出的,整个
case 语句群就是一块完整而连续的代码,只是switch让其从不同的位置开始执行。

上面的内容,在《Computer Systems A Programmer's Perspective》中有很详细的论述,
感兴趣可以去找来仔细看看~~~

既然,case 语句需要的是整数的常量值,那么我们是否可用 const 类型呢?比如下面
一段代码:

const int c_1 = 100 ;
const int c_2 = 102 ;

void test( int x )
{
switch( x ){
case c_1 :
++x ;
case c_2 :
--x ;
}
}

这段代码,用 c 编译器编译,编译器会提示错误,但在 c++ 编译器中却不会,这主要是由于 c , 与 c++ 编译器对 const 这个东东的处理不同。我们来看看下面一段 c 程序
/*------------- filename : const_c.c -----------*/
const int a = 15 ;

void f( int x )
{
x = a ;
}
同样用 gcc 编译
gcc -S const_c.c
然后,来看看它的汇编文件
.file "const_c.c"
.globl a
.section .rodata
.align 4
.type a,@object
.size a,4
a: // 1
.long 15
.text
.globl f
.type f,@function
f:
pushl %ebp
movl %esp, %ebp
movl a, %eax // 2
movl %eax, 8(%ebp)
leave
ret
.Lfe1:
.size f,.Lfe1-f
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
注意 // 1 处,C 编译器为 a 分配了地址,并把它的值设为 15 ,而在 // 2 处,它是将
a 这个地址中的值赋给了 %eax,这同一般的普通变量而非const 变量赋值没什么两样

下面我们用 c++ 编译器来编译这段代码,它产生的汇编文件如下:
.file "const_cpp.cpp"
.text
.align 2
.globl _Z1fi
.type _Z1fi,@function
_Z1fi:
.LFB2:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
movl $15, 8(%ebp) // 1
leave
ret
.LFE2:
.Lfe1:
.size _Z1fi,.Lfe1-_Z1fi
.section .rodata
.align 4
.type a,@object
.size a,4
a:
.long 15
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
同样注意// 1 处,它以经把 a 的值用 15 来取代了,
也就是说,在c中const变量的行为更像一个非const变量,而在cpp中,const变量的行为就像是#define
由于 c++ 中,const 变量的值是在编译时就计算出来的,因此,它可以用在 case 语句中,而 c 中,const值在编译时只是一个变量的地址,因此,它无法用在 case 语句中.
-----------------------------------------------------------------------------
参考文献:<<Computer Systems A Programmer's Perspective>>
转载请注明原作者,以出处~~
lovesnow1314 2007-04-10
  • 打赏
  • 举报
回复
标 题: 更深入一点理解switch语句及c/c++对const的处理
发信站: BBS 水木清华站 (Thu Feb 24 20:58:16 2005), 站内

更深入一点理解 switch 语句 及 c/c++ 对 const 的处理
谢煜波


前段时间在论坛上看见台湾李维在<<Borland传奇>>一书中对windows编程模式中,消息处理部分有如下的一些分析:
他说,在消息处理循环中,一般的形式是这样的
MSG msg ;
switch( msg ){
case WM_XXXXXXX :
....
case WM_XXXXXXX :
....
case WM_XXXXXXX :
....
} ;
李维说,这种模式是很低效的,因应经过汇编后,这种C代码会产生如下的汇编代码
cmp .... .....
jnz .... .....
cmp .... .....
jnz .... .....
cmp .... .....
jnz .... .....
如果你的 case 足够多,比如,你有一万条消息需要处理,而不幸的是你把一条最常用的消息
放在了最后一位,那么当这条消息要得到处理,会首先经过一万次的cmp与jnz, 李维认为,这
是非常非常低效的,实在是低效的忍无可忍,无需再忍~~:P

在起初,我也是这样认为的,但近来的阅读及实验却发现,这种看法非常片面,今天就来谈谈这个问题( 所有实验在 linux 平台下完成 )

首先看一到用 c 编写的程序
/* -------------------- filename : ta.c --------------- */
int switch_test_first( int x )
{
int res ;
switch( x ){
case 100 :
res = 1 ;
break ;
case 102 :
res = 2 ;
break ;
case 103 :
res = 3 ;
break ;
}
return res ;
}
然后,我们用 gcc 将它编译成汇编文件( 使用 -S 开关 )
gcc -S ta.c
将得到如下的汇编文件( ta.s )
.file "ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
.file "ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
movl %eax, -8(%ebp)
cmpl $102, -8(%ebp) // 1
je .L4 // 2
cmpl $102, -8(%ebp) // 3
jg .L8 // 4
cmpl $100, -8(%ebp) // 5
je .L3 // 6
jmp .L2 // 7
.L8:
cmpl $103, -8(%ebp)
je .L5
jmp .L2
.L3:
movl $1, -4(%ebp)
jmp .L2
.L4:
movl $2, -4(%ebp)
jmp .L2
.L5:
movl $3, -4(%ebp)
.L2:
movl -4(%ebp), %eax
leave
ret
.Lfe1:
.size switch_test_first,.Lfe1-switch_test_first
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

注意看文件中 // 1 ~ // 7 的部份,从这个部份,我们可以看出,gcc确实是把一些case语句转成了李维所说的那种方式进行处理,我们看见了代码中存在有众多的 cmpl 与 jmp 语句
这就相当于你使用if..else..一样,但是否总是这样呢?

我们下面改动一下 ta.c 这个文件,在里面再多加一些 case 语句
/* -------------- filename : new_ta.c ------------------- */
int switch_test_first( int x )
{
int res ;
switch( x ){
case 100 :
res = 1 ;
break ;
case 102 :
res = 2 ;
break ;
case 103 :
res = 3 ;
break ;
case 104 :
res = 4 ;
break ;
case 105 :
res = 5 ;
break ;
case 106 :
res = 6 ;
break ;
}
return res ;
}
这个 new_ta.c 与原来的 ta.c 在结构上完全相同,唯一不同的就是 case 语句的数量变多了,下面我们来编译一下这个文件
gcc -S new_ta.c
下面是我们产生的更新的汇编文件
.file "new_ta.c"
.text
.globl switch_test_first
.type switch_test_first,@function
switch_test_first:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
subl $100, %eax
movl %eax, -8(%ebp)
cmpl $6, -8(%ebp)
ja .L2
movl -8(%ebp), %edx
movl .L9(,%edx,4), %eax
jmp *%eax
.section .rodata
.align 4
.align 4
.L9: // A
.long .L3
.long .L2
.long .L4
.long .L5
.long .L6
.long .L7
.long .L8
.text
.L3: // 1
movl $1, -4(%ebp)
jmp .L2
.L4: // 2
movl $2, -4(%ebp)
jmp .L2
.L5: // 3
movl $3, -4(%ebp)
jmp .L2 // 4
.L6:
movl $4, -4(%ebp)
jmp .L2 // 5
.L7:
movl $5, -4(%ebp) // 6
jmp .L2
.L8: // 7
movl $6, -4(%ebp)
.L2:
movl -4(%ebp), %eax
leave
ret
.Lfe1:
.size switch_test_first,.Lfe1-switch_test_first
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

fohonet 2007-04-10
  • 打赏
  • 举报
回复
等待 高手 ~~~~~~~~~~~~~
freshui 2007-04-10
  • 打赏
  • 举报
回复
这个有点难度..
fohonet 2007-04-10
  • 打赏
  • 举报
回复
可以说明白点 吗 ?
AWolfBoy 2007-04-10
  • 打赏
  • 举报
回复
跳表法
fohonet 2007-04-10
  • 打赏
  • 举报
回复
自己测试 了 一下

应该 是这样 的
当Type的值为:2x2, 3x3, 4x4 ,5x5中的任何一个时候, EnterType = Type

否则:EnterType = 2x2;


bargio_susie 2007-04-10
  • 打赏
  • 举报
回复
收藏了。。。
加载更多回复(2)

69,373

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧