5.5 难以构成跳转表的switch(2)
在代码清单5-15中,从case 1开始到case 255结束,共255个case值,会生成一个255字节大小索引表。其中从6到255间隔了249个case值, 这249项保存的是case语句块地址表中switch的结尾地址下标,如代码清单5-16所示。
代码清单5-16 非线性索引表—Debug版
- switch(nIndex){ // 源码对比
- 0040DF80 mov ecx,dword ptr [ebp-4]
- 0040DF83 mov dword ptr [ebp-8],ecx
- ; 这三条指令为取出变量nIndex的值并保存到edx的操作
- 0040DF86 mov edx,dword ptr [ebp-8]
- ; 索引表以0下标开始,case最小标号值为1,需要进行减1调整
- 0040DF89 sub edx,1
- ; 将对齐下标后的值放回到临时变量ebp-8中
- 0040DF8C mov dword ptr [ebp-8],edx
- ; 将临时变量与254进行无符号比较,若临时变量大于254则跳转
- ; 跳转到地址0x0040e002处,那里是switch结构的结尾
- 0040DF8F cmp dword ptr [ebp-8],0FEh
- 0040DF96 ja $L566+0Dh (0040e002)
- ; switch的参数值在case值范围内,取出临时变量中的数据并保存到ecx中
- 0040DF98 mov ecx,dword ptr [ebp-8]
- ; 清空eax的值,以ecx为下标在索引表中取出1字节的内容放入al中
- ; 地址0x0040E02F为索引表的首地址,查看图5-5
- 0040DF9B xor eax,eax
- ; 从索引表中取出对应地址表的下标
- 0040DF9D mov al,byte ptr (0040e02f)[ecx]
- ; 以eax作下标,0x0040E013为基址进行寻址,跳转到该地址处
- ; 地址0x0040E013为case语句块地址表的首地址,查看图5-5
- 0040DFA3 jmp dword ptr [eax*4+40E013h]
- case 1: printf("nIndex == 1"); // 源码对比
- 0040DFAA push offset string "nIndex == 1" (00421024)
- 0040DFAF call printf (004014b0)
- 0040DFB4 add esp,4
- break; // 源码对比
- 0040DFB7 jmp $L566+0Dh (0040e002)
- case 2: printf("nIndex == 2"); // 源码对比
- 0040DFB9 push offset string "nIndex == 2" (0042003c)
- 0040DFBE call printf (004014b0)
- 0040DFC3 add esp,4
- break; // 源码对比
- 0040DFC6 jmp $L566+0Dh (0040e002)
- case 3: printf("nIndex == 3"); // 源码对比
- 0040DFC8 push offset string "nIndex == 3" (004210d8)
- 0040DFCD call printf (004014b0)
- 0040DFD2 add esp,4
- break; // 源码对比
- 0040DFD5 jmp $L566+0Dh (0040e002)
- case 5: printf("nIndex == 5"); // 源码对比
- 0040DFD7 push offset string "i == 3" (00420028)
- 0040DFDC call printf (004014b0)
- 0040DFE1 add esp,4
- break; // 源码对比
- 0040DFE4 jmp $L566+0Dh (0040e002)
- case 6: printf("nIndex == 6");
- 0040DFE6 push offset string "nIndex == 6" (004210cc)
- 0040DFEB call printf (004014b0)
- 0040DFF0 add esp,4
- break; // 源码对比
- 0040DFF3 jmp $L566+0Dh (0040e002)
- case 255: printf("nIndex == 255"); // 源码对比
- 0040DFF5 push offset string "nIndex == 255" (0042005c)
- 0040DFFA call printf (004014b0)
- 0040DFFF add esp,4
- break; } // 源码对比
- ; switch结束地址
- 0040E002 pop edi
代码清单5-16首先查询索引表,索引表由数组组成,数组的每一项大小为1字节。从索引表中取出地址表的下标,根据下标值,找到跳转地址表中对应的case语句块首地址,跳转到该地址处。这种查询方式会产生两次间接内存访问,在效率上低于线性表方式。
|
| 图5-7 非线性索引表—Debug版 |