一、switch...case...的格式
1 switch(表达式)
2 {
3 case 常量表达式1:
4 语句;
5 break;
6 case 常量表达式2:
7 语句;
8 break;
9 case 常量表达式3:
10 语句;
11 break;
12 case 常量表达式3:
13 语句;
14 break;
15 default:
16 语句;
17 break;
18 }
switch要求:
1、case后面必须是常量表达式
2、case后常量表达式的值不能一样
3、switch后面表达式必须为整数,不能为浮点数
4、case后的语句可以有多个且不用花括号括起来;
5、case和default子句的先后顺序可以先后变动,default子句可以省略不用;
二、switch...case...与if...else...的区别
1、switch语句 是if语句的简写,我们可以看一下分析,
1 void Function(int m)
2 {
3 if (m == 1)
4 {
5 printf("%d\n", 1);
6 }
7 else if (m == 2)
8 {
9 printf("%d\n", 2);
10 }
11 else if (m == 3)
12 {
13 printf("%d\n", 3);
14 }
15 else if (m == 4)
16 {
17 printf("%d\n", 4);
18 }
19 else if (m == 5)
20 {
21 printf("%d\n", 5);
22 }
23 }
24
25 int main(int argc, char* argv[])
26 {
27 Function(3);
28 return 0;
29 }
反汇编代码如下
1 00401020 push ebp
2 00401021 mov ebp,esp
3 00401023 sub esp,40h
4 00401026 push ebx
5 00401027 push esi
6 00401028 push edi
7 00401029 lea edi,[ebp-40h]
8 0040102C mov ecx,10h
9 00401031 mov eax,0CCCCCCCCh
10 00401036 rep stos dword ptr [edi]
11 00401038 cmp dword ptr [ebp+8],1
12 0040103C jne Function+2Fh (0040104f)
13 0040103E push 1
14 00401040 push offset string "%d\n" (0042201c)
15 00401045 call printf (00401150)
16 0040104A add esp,8
17 0040104D jmp Function+89h (004010a9)
18 0040104F cmp dword ptr [ebp+8],2
19 00401053 jne Function+46h (00401066)
20 00401055 push 2
21 00401057 push offset string "%d\n" (0042201c)
22 0040105C call printf (00401150)
23 00401061 add esp,8
24 00401064 jmp Function+89h (004010a9)
25 00401066 cmp dword ptr [ebp+8],3
26 0040106A jne Function+5Dh (0040107d)
27 0040106C push 3
28 0040106E push offset string "%d\n" (0042201c)
29 00401073 call printf (00401150)
30 00401078 add esp,8
31 0040107B jmp Function+89h (004010a9)
32 0040107D cmp dword ptr [ebp+8],4
33 00401081 jne Function+74h (00401094)
34 00401083 push 4
35 00401085 push offset string "%d\n" (0042201c)
36 0040108A call printf (00401150)
37 0040108F add esp,8
38 00401092 jmp Function+89h (004010a9)
39 00401094 cmp dword ptr [ebp+8],5
40 00401098 jne Function+89h (004010a9)
41 0040109A push 5
42 0040109C push offset string "%d\n" (0042201c)
43 004010A1 call printf (00401150)
44 004010A6 add esp,8
45 004010A9 pop edi
46 004010AA pop esi
47 004010AB pop ebx
48 004010AC add esp,40h
49 004010AF cmp ebp,esp
50 004010B1 call __chkesp (004011d0)
51 004010B6 mov esp,ebp
52 004010B8 pop ebp
53 004010B9 ret
下面来一段相同功能的switch语句,再观察反汇编
1 #include "stdafx.h"
2
3 void Function(int m)
4 {
5 switch (m)
6 {
7 case 1:
8 printf("1\n");
9 break;
10 case 2:
11 printf("2\n");
12 break;
13 case 3:
14 printf("3\n");
15 break;
16 default:
17 printf("Error!\n");
18 break;
19 }
20 }
21
22 int main(int argc, char* argv[])
23 {
24 Function(3);
25 return 0;
26 }
反汇编代码如下
1 00401020 push ebp
2 00401021 mov ebp,esp
3 00401023 sub esp,44h
4 00401026 push ebx
5 00401027 push esi
6 00401028 push edi
7 00401029 lea edi,[ebp-44h]
8 0040102C mov ecx,11h
9 00401031 mov eax,0CCCCCCCCh
10 00401036 rep stos dword ptr [edi]
11 00401038 mov eax,dword ptr [ebp+8]
12 0040103B mov dword ptr [ebp-4],eax
13 0040103E cmp dword ptr [ebp-4],1
14 00401042 je Function+32h (00401052)
15 00401044 cmp dword ptr [ebp-4],2
16 00401048 je Function+41h (00401061)
17 0040104A cmp dword ptr [ebp-4],3
18 0040104E je Function+50h (00401070)
19 00401050 jmp Function+5Fh (0040107f)
20 00401052 push offset string "1\n" (00422f54)
21 00401057 call printf (00401150)
22 0040105C add esp,4
23 0040105F jmp Function+6Ch (0040108c)
24 00401061 push offset string "4\n" (0042212c)
25 00401066 call printf (00401150)
26 0040106B add esp,4
27 0040106E jmp Function+6Ch (0040108c)
28 00401070 push offset string "5\n" (0042201c)
29 00401075 call printf (00401150)
30 0040107A add esp,4
31 0040107D jmp Function+6Ch (0040108c)
32 0040107F push offset string "Error!\n" (00422fa4)
33 00401084 call printf (00401150)
34 00401089 add esp,4
35 0040108C pop edi
36 0040108D pop esi
37 0040108E pop ebx
38 0040108F add esp,44h
39 00401092 cmp ebp,esp
40 00401094 call __chkesp (004011d0)
41 00401099 mov esp,ebp
42 0040109B pop ebp
43 0040109C ret
观察反汇编代码可以看到,此时判定状态差别不大,均是cmp,jmp
三、switch语句大表的产生
1、基于上例子的基础上,添加case后面的值,一个一个增加,观察反汇编代码的变化(何时生成大表).
上个例子中,switch语句中,只有3个分支,我们增加一个case看看
1 #include "stdafx.h"
2
3 void Function(int m)
4 {
5 switch (m)
6 {
7 case 1:
8 printf("1\n");
9 break;
10 case 2:
11 printf("2\n");
12 break;
13 case 3:
14 printf("3\n");
15 break;
16 case 4:
17 printf("4\n");
18 break;
19 default:
20 printf("Error!\n");
21 break;
22 }
23 }
24
25 int main(int argc, char* argv[])
26 {
27 Function(3);
28 return 0;
29 }
1 00401020 push ebp
2 00401021 mov ebp,esp
3 00401023 sub esp,44h
4 00401026 push ebx
5 00401027 push esi
6 00401028 push edi
7 00401029 lea edi,[ebp-44h]
8 0040102C mov ecx,11h
9 00401031 mov eax,0CCCCCCCCh
10 00401036 rep stos dword ptr [edi]
11 00401038 mov eax,dword ptr [ebp+8]
12 0040103B mov dword ptr [ebp-4],eax
13 0040103E mov ecx,dword ptr [ebp-4]
14 00401041 sub ecx,1
15 00401044 mov dword ptr [ebp-4],ecx
16 00401047 cmp dword ptr [ebp-4],3
17 0040104B ja $L539+0Fh (00401093)
18 0040104D mov edx,dword ptr [ebp-4]
19 00401050 jmp dword ptr [edx*4+4010B1h]
20 $L533:
21 00401057 push offset string "1\n" (00422034)
22 0040105C call printf (00401140)
23 00401061 add esp,4
24 00401064 jmp $L539+1Ch (004010a0)
25 $L535:
26 00401066 push offset string "2\n" (00422030)
27 0040106B call printf (00401140)
28 00401070 add esp,4
29 00401073 jmp $L539+1Ch (004010a0)
30 $L537:
31 00401075 push offset string "3\n" (0042202c)
32 0040107A call printf (00401140)
33 0040107F add esp,4
34 00401082 jmp $L539+1Ch (004010a0)
35 $L539:
36 00401084 push offset string "4\n" (00422028)
37 00401089 call printf (00401140)
38 0040108E add esp,4
39 00401091 jmp $L539+1Ch (004010a0)
40 00401093 push offset string "Error!\n" (0042201c)
41 00401098 call printf (00401140)
42 0040109D add esp,4
00401038 mov eax,dword ptr [ebp+8] //将参数m放置eax
0040103B mov dword ptr [ebp-4],eax //将eax的值给到局部变量ebp-4
0040103E mov ecx,dword ptr [ebp-4] //局部变量的值给ecx
00401041 sub ecx,1 //ecx的值减去case中最小的常量值
00401044 mov dword ptr [ebp-4],ecx //再将ecx的值给局部变量,此时ecx的值为2
00401047 cmp dword ptr [ebp-4],3 //将局部变量m与3比较,以至于为什么是跟3比较呢?首先会有一张表,这个表记录了每个case分支的函数地址,这个表的首地址是以最下的case + 常量值为首,如果超过这个表,那么则都跳转至default处
0040104B ja $L539+0Fh (00401093) //如果高于等于的话,函数则跳转至0x00401096地址处,这个地址其实就是default的位置
0040104D mov edx,dword ptr [ebp-4] //将局部变量ebp-4,m的值赋给edx
00401050 jmp dword ptr [edx*4+4010B1h] //大表根据这个差值来跳转值那个函数地址
2、将1中的常量值的顺序打乱,观察反汇编代码(观察顺序是否会影响生成大表).
为了方便,我增加几句case
1 #include "stdafx.h"
2
3 void Function(int m)
4 {
5 switch (m)
6 {
7 case 9:
8 printf("9\n");
9 break;
10 case 3:
11 printf("3\n");
12 break;
13 case 7:
14 printf("7\n");
15 break;
16 case 4:
17 printf("4\n");
18 break;
19 case 1:
20 printf("1\n");
21 break;
22 case 2:
23 printf("2\n");
24 break;
25 case 6:
26 printf("6\n");
27 break;
28 default:
29 printf("Error!\n");
30 break;
31 }
32 }
33
34 int main(int argc, char* argv[])
35 {
36 Function(4);
37 return 0;
38 }
1 0040D7D0 push ebp
2 0040D7D1 mov ebp,esp
3 0040D7D3 sub esp,44h
4 0040D7D6 push ebx
5 0040D7D7 push esi
6 0040D7D8 push edi
7 0040D7D9 lea edi,[ebp-44h]
8 0040D7DC mov ecx,11h
9 0040D7E1 mov eax,0CCCCCCCCh
10 0040D7E6 rep stos dword ptr [edi]
11 0040D7E8 mov eax,dword ptr [ebp+8]
12 0040D7EB mov dword ptr [ebp-4],eax
13 0040D7EE mov ecx,dword ptr [ebp-4]
14 0040D7F1 sub ecx,1
15 0040D7F4 mov dword ptr [ebp-4],ecx
16 0040D7F7 cmp dword ptr [ebp-4],8
17 0040D7FB ja $L545+0Fh (0040d870)
18 0040D7FD mov edx,dword ptr [ebp-4]
19 0040D800 jmp dword ptr [edx*4+40D88Eh]
20 $L533:
21 0040D807 push offset string "9\n" (00422f6c)
22 0040D80C call printf (00401140)
23 0040D811 add esp,4
24 0040D814 jmp $L545+1Ch (0040d87d)
25 $L535:
26 0040D816 push offset string "3\n" (00422144)
27 0040D81B call printf (00401140)
28 0040D820 add esp,4
29 0040D823 jmp $L545+1Ch (0040d87d)
30 $L537:
31 0040D825 push offset string "7\n" (00422034)
32 0040D82A call printf (00401140)
33 0040D82F add esp,4
34 0040D832 jmp $L545+1Ch (0040d87d)
35 $L539:
36 0040D834 push offset string "4\n" (00422024)
37 0040D839 call printf (00401140)
38 0040D83E add esp,4
39 0040D841 jmp $L545+1Ch (0040d87d)
40 $L541:
41 0040D843 push offset string "1\n" (00422030)
42 0040D848 call printf (00401140)
43 0040D84D add esp,4
44 0040D850 jmp $L545+1Ch (0040d87d)
45 $L543:
46 0040D852 push offset string "2\n" (0042202c)
47 0040D857 call printf (00401140)
48 0040D85C add esp,4
49 0040D85F jmp $L545+1Ch (0040d87d)
50 $L545:
51 0040D861 push offset string "5\n" (00422028)
52 0040D866 call printf (00401140)
53 0040D86B add esp,4
54 0040D86E jmp $L545+1Ch (0040d87d)
55 0040D870 push offset string "Error!\n" (0042201c)
56 0040D875 call printf (00401140)
57 0040D87A add esp,4
观察大表,以及反汇编代码,很显然,switch语句中,case的顺序不会影响到大表的生成,大表的生成根据地址而来
3、将case后面的值改成从100开始到109,观察汇编变化(观察值较大时是否生成大表).
1 #include "stdafx.h"
2
3 void Function(int m)
4 {
5 switch (m)
6 {
7 case 101:
8 printf("101\n");
9 break;
10 case 102:
11 printf("102\n");
12 break;
13 case 103:
14 printf("103\n");
15 break;
16 case 104:
17 printf("104\n");
18 break;
19 case 105:
20 printf("105\n");
21 break;
22 case 106:
23 printf("106\n");
24 break;
25 case 107:
26 printf("107\n");
27 break;
28 case 108:
29 printf("108\n");
30 break;
31 case 109:
32 printf("109\n");
33 break;
34 case 110:
35 printf("110\n");
36 break;
37 default:
38 printf("Error!\n");
39 break;
40 }
41 }
42
43 int main(int argc, char* argv[])
44 {
45 Function(104);
46 return 0;
47 }
4、将连续的10项中抹去1项或者2项,观察反汇编有无变化(观察大表空缺位置的处理)
1 #include "stdafx.h"
2
3 void Function(int m)
4 {
5 switch (m)
6 {
7 case 101:
8 printf("101\n");
9 break;
10 /*
11 case 102:
12 printf("102\n");
13 break;
14 */
15 case 103:
16 printf("103\n");
17 break;
18 case 104:
19 printf("104\n");
20 break;
21 case 105:
22 printf("105\n");
23 break;
24 case 106:
25 printf("106\n");
26 break;
27 case 107:
28 printf("107\n");
29 break;
30 case 108:
31 printf("108\n");
32 break;
33 case 109:
34 printf("109\n");
35 break;
36 case 110:
37 printf("110\n");
38 break;
39 default:
40 printf("Error!\n");
41 break;
42 }
43 }
44
45 int main(int argc, char* argv[])
46 {
47 Function(104);
48 return 0;
49 }
抹去三项看看?
1 #include "stdafx.h"
2
3 void Function(int m)
4 {
5 switch (m)
6 {
7 case 101:
8 printf("101\n");
9 break;
10 /*
11 case 102:
12 printf("102\n");
13 break;
14 case 103:
15 printf("103\n");
16 break;
17 case 104:
18 printf("104\n");
19 break;
20 */
21 case 105:
22 printf("105\n");
23 break;
24 case 106:
25 printf("106\n");
26 break;
27 case 107:
28 printf("107\n");
29 break;
30 case 108:
31 printf("108\n");
32 break;
33 case 109:
34 printf("109\n");
35 break;
36 case 110:
37 printf("110\n");
38 break;
39 default:
40 printf("Error!\n");
41 break;
42 }
43 }
44
45 int main(int argc, char* argv[])
46 {
47 Function(104);
48 return 0;
49 }
经过上面两个例子可以看出,被抹除掉的项按照default来处理了,我们再抹去两项看看
依旧还是生成大表,我们接着再抹除两个,试试看,看编译器是否还是给我们生成大表?
5、在10项中连续抹去,不要抹去最大值和最小值(观察何时生成小表).
1 #include "stdafx.h"
2
3 void Function(int m)
4 {
5 switch (m)
6 {
7 case 101:
8 printf("101\n");
9 break;
10 /*
11 case 102:
12 printf("102\n");
13 break;
14 case 103:
15 printf("103\n");
16 break;
17 case 104:
18 printf("104\n");
19 break;
20 case 105:
21 printf("105\n");
22 break;
23 case 106:
24 printf("106\n");
25 break;
26 case 107:
27 printf("107\n");
28 break;
29 */
30 case 108:
31 printf("108\n");
32 break;
33 case 109:
34 printf("109\n");
35 break;
36 case 110:
37 printf("110\n");
38 break;
39 default:
40 printf("Error!\n");
41 break;
42 }
43 }
44
45 int main(int argc, char* argv[])
46 {
47 Function(109);
48 return 0;
49 }
可以看到,由于编译器只采用dl用来存储小表,所以当间隔255个数之后,小表也将不负存在了
6、将case后面常量表达式改成毫不连续的值,观察反汇编变化.
这种switch...case..语句其实基本见不着,因为丝毫无意义
1 #include "stdafx.h"
2
3 void Function(int m)
4 {
5 switch (m)
6 {
7 case 5:
8 printf("101\n");
9 break;
10 case 888:
11 printf("108\n");
12 break;
13 case 3200:
14 printf("109\n");
15 break;
16 case 5000:
17 printf("110\n");
18 break;
19 case 9000:
20 printf("110\n");
21 break;
22 case 19200:
23 printf("110\n");
24 break;
25 default:
26 printf("Error!\n");
27 break;
28 }
29 }
30
31 int main(int argc, char* argv[])
32 {
33 Function(109);
34 return 0;
35 }
1 00401020 push ebp
2 00401021 mov ebp,esp
3 00401023 sub esp,44h
4 00401026 push ebx
5 00401027 push esi
6 00401028 push edi
7 00401029 lea edi,[ebp-44h]
8 0040102C mov ecx,11h
9 00401031 mov eax,0CCCCCCCCh
10 00401036 rep stos dword ptr [edi]
11 00401038 mov eax,dword ptr [ebp+8]
12 0040103B mov dword ptr [ebp-4],eax
13 0040103E cmp dword ptr [ebp-4],1388h
14 00401045 jg Function+4Ah (0040106a)
15 00401047 cmp dword ptr [ebp-4],1388h
16 0040104E je Function+8Bh (004010ab)
17 00401050 cmp dword ptr [ebp-4],5
18 00401054 je Function+5Eh (0040107e)
19 00401056 cmp dword ptr [ebp-4],378h
20 0040105D je Function+6Dh (0040108d)
21 0040105F cmp dword ptr [ebp-4],0C80h
22 00401066 je Function+7Ch (0040109c)
23 00401068 jmp Function+0B8h (004010d8)
24 0040106A cmp dword ptr [ebp-4],2328h
25 00401071 je Function+9Ah (004010ba)
26 00401073 cmp dword ptr [ebp-4],4B00h
27 0040107A je Function+0A9h (004010c9)
28 0040107C jmp Function+0B8h (004010d8)
29 0040107E push offset string "101\n" (00422040)
30 00401083 call printf (00401160)
31 00401088 add esp,4
32 0040108B jmp Function+0C5h (004010e5)
33 0040108D push offset string "108\n" (00422038)
34 00401092 call printf (00401160)
35 00401097 add esp,4
36 0040109A jmp Function+0C5h (004010e5)
37 0040109C push offset string "109\n" (00422030)
38 004010A1 call printf (00401160)
39 004010A6 add esp,4
40 004010A9 jmp Function+0C5h (004010e5)
41 004010AB push offset string "110\n" (00422028)
42 004010B0 call printf (00401160)
43 004010B5 add esp,4
44 004010B8 jmp Function+0C5h (004010e5)
45 004010BA push offset string "110\n" (00422028)
46 004010BF call printf (00401160)
47 004010C4 add esp,4
48 004010C7 jmp Function+0C5h (004010e5)
49 004010C9 push offset string "110\n" (00422028)
50 004010CE call printf (00401160)
51 004010D3 add esp,4
52 004010D6 jmp Function+0C5h (004010e5)
53 004010D8 push offset string "Error!\n" (0042201c)
54 004010DD call printf (00401160)
55 004010E2 add esp,4
56 004010E5 pop edi
57 004010E6 pop esi
58 004010E7 pop ebx
59 004010E8 add esp,44h