C-switch语句反汇编

switch

1、switch表达式里面的结果必须是整数

2、同一个switch语句的case后面的常量表达式不能出现相同结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "stdafx.h"


int main(int argc, char* argv[])
{
int x=1;
switch(x)
{

case 1:
printf("1");
break;
case 2:
printf("2");
break;
case 3:
printf("3");
break;
default:
printf("default");
break;

}

getchar();
return 0;

}

image-20231108142751956

反汇编

大表

1、分支少的时候(根据编译器自己的算法来)使用switch没有意义,编译器在这时候生成的汇编代码与if没有显著区别

2、case后跟的常量可以是无需的,不影响大表生成

那么问题来了,什么是大表,大表是当switch分支语句超过四条的时候编译器为了提高效率,避免逐条比较而在内存中存放的一个特殊数据块,这些数据记录了各个分支语句跳转的内存地址,可以通过表达式计算出结果,对比大表实现快速跳转

如果case后面的常量差值过大,编译器会自己衡量是否生成大表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include "stdafx.h"

void fun(int x)
{
switch(x)
{

case 1:
printf("1");
break;
case 2:
printf("2");
break;
case 3:
printf("3");
break;
case 4:
printf("4");
break;
default:
printf("default");
break;

}
}

int main(int argc, char* argv[])
{

fun(3);
getchar();
return 0;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
0040BE98   mov         eax,dword ptr [ebp+8]
0040BE9B mov dword ptr [ebp-4],eax
0040BE9E mov ecx,dword ptr [ebp-4]
0040BEA1 sub ecx,1

0040BEA4 mov dword ptr [ebp-4],ecx
0040BEA7 cmp dword ptr [ebp-4],3
0040BEAB ja $L539+0Fh (0040bef3)
0040BEAD mov edx,dword ptr [ebp-4]
0040BEB0 jmp dword ptr [edx*4+40BF11h]

$L533:
0040BEB7 push offset string "1" (0041ff88)
0040BEBC call printf (0040bc20)
0040BEC1 add esp,4
0040BEC4 jmp $L539+1Ch (0040bf00)

$L535:
0040BEC6 push offset string "2" (0041ff80)
0040BECB call printf (0040bc20)
0040BED0 add esp,4
0040BED3 jmp $L539+1Ch (0040bf00)

$L537:
0040BED5 push offset string "3" (0041f01c)
0040BEDA call printf (0040bc20)
0040BEDF add esp,4
0040BEE2 jmp $L539+1Ch (0040bf00)

$L539:
0040BEE4 push offset string "china" (0041ff84)
0040BEE9 call printf (0040bc20)
0040BEEE add esp,4
0040BEF1 jmp $L539+1Ch (0040bf00)
0040BEF3 push offset string "default" (0041ff78)
0040BEF8 call printf (0040bc20)
0040BEFD add esp,4
0040BF00 pop edi
0040BF01 pop esi
0040BF02 pop ebx
0040BF03 add esp,44h
0040BF06 cmp ebp,esp
0040BF08 call __chkesp (0040b4c0)
0040BF0D mov esp,ebp
0040BF0F pop ebp
0040BF10 ret

使用switch语句的时候最好连续,如果有不连续的数值并且有默认语句的时候,在空缺的位置大表里记录的将会是默认语句的内存地址

小表

如果在生成大表以后,中间不连续的数值超过寄存器自己的限制,将会生成一张小表用于辅助查询

小表配合大表可以节省空间,防止浪费,具体算法不要深究

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include "stdafx.h"

void fun(int x)
{
switch(x)
{

case 1:
printf("1");
break;
case 9:
printf("9");
break;
case 10:
printf("10");
break;
case 11:
printf("11");
break;
default:
printf("default");
break;

}
}

int main(int argc, char* argv[])
{

fun(3);
getchar();
return 0;

}