switch(情况){
case 1:
//执行代码;
break;
case 2:
//执行代码;
break;
case 3:
//执行代码;
break;
case 4:
//执行代码;
break;
default:
//如果没有以上的情况,则执行default中的语句
break;
}
以上是switch使用的基本格式,它会通过输入的情况跳转到对应的 case语句处,然后往下执行。如有特殊情况则执行default。
switch的反汇编
当switch当中的case(情况)语句小于4条时,它的反汇编和if语句基本一样。反汇编是通过cmp去和情况一一做比较然后je跳转。但当case(情况)语句大于3条的时候,它会选择直接通过参数计算出对应程序的地址。
//汇编代码
mov eax,dword ptr [ebp+8] //将我们传入的参数(ebp+8)给到寄存器eax中
mov dword ptr [ebp-4],eax //讲传入的参数放入ebp-4(计数器、计数)
mov ecx,dword ptr [ebp-4] //把这个计数放入ecx中进行情况查看
sub ecx,1 //编译器认为最小的情况认为是0,这样便于安排地址(更节省空间)
mov dword ptr [ebp-4],ecx //将寄存器认为情况值放入计数器中
cmp dword ptr [ebp-4],3 //将输入的情况与编译器认为的最大情况进行比较看是否超过
ja xxxxxxxx //如果大于了最大情况,那么我们直接结束该指令
mov edx,dword ptr [ebp-4] //将计数器中的值放入到edx中,方便计算指令地址
jmp dword ptr [edx*4+基址] //通过edx*4加上基址,进行偏移寻址,跳转到对应情况的指令地址处(生成大表)
情况0的指令地址处
情况1的指令地址处
情况2的指令地址处
情况3的指令地址处
情况4的指令地址处
xxxxxxxx //ja跳转到的指定处
小结:
[edx*4+基址]生成大表,通过表查询地址
1、如果我们设置的情况值是连续的,则编译器为了节省内存会通过[edx*4+基址]的方式来跳转到对应情况处的指令地址(连续分配)。在设置情况值时可以是无序的。(先设case5,再设case1这些都可以)
2、如果我们设置的情况值不是连续的,或者小于四种情况,那么为了提高效率,编译器会选择直接用je语句直接判断跳转。
3、当我们最小的情况值为n的时候,我们的 sub ecx ,n 语句也会相应的减n
4、ja是一个很神奇的跳转,只有当CF(进位标志寄存器)和ZF(进位标志寄存器)都为0时才不跳转,有一个不为0就跳转。说这个是为了解释它是怎么判断我们传入的数正好是在我们设置的连续的情况值范围内的。
5、当我连续的情况值中间出现了中断之后,编译器会选择将default的地址放入到中断位置。如果没有default,那么就存储switch外的一个地址。
6、但是当我们中断的范围太大时,(用default的地址填表太浪费空间资源了)这时候就会产生小表。
//产生小表
xor edx,edx //清空edx寄存器用来存储小表地址
mov dl,byte ptr (小表)[eax] //在小表通过eax来偏移找到大表的偏移量就得到了对应情况的指令地址。相当于我只用了一个字节去记录指令地址的偏移量
jmp dword ptr [edx*4+大表基址]
7、小表最大256个字节,如果偏移超过了这个大小就会放弃用小表记录偏移
8、当情况值之间有巨大间隔时,编译器会选择二叉树查找的方法