switch汇编语句分析包含以下四种不同的情况:
1.当case分支较少的时候
2.当case分支较多且连续的时候
3.当case分支较多且连续,中间删除某一个分支的时候
4.当case分支较多且连续,中间删除多个分支的时候
情况一:当case当中的分支语句较少的时候,其对应的汇编分析
#include<stdlib.h>
#include<stdio.h>
void function(int a) {
switch (a)
{
case 1:
printf("1");
break;
case 2:
printf("2");
break;
default:
printf("3");
break;
}
}
int main() {
function(2);
//function(1);
return 0;
}
对应的汇编程序为:
void function(int a) {
00007FF7704D18D0 mov dword ptr [rsp+8],ecx
00007FF7704D18D4 push rbp
00007FF7704D18D5 push rdi
00007FF7704D18D6 sub rsp,0F8h
00007FF7704D18DD lea rbp,[rsp+20h]
00007FF7704D18E2 mov rdi,rsp
00007FF7704D18E5 mov ecx,3Eh
00007FF7704D18EA mov eax,0CCCCCCCCh
00007FF7704D18EF rep stos dword ptr [rdi]
00007FF7704D18F1 mov ecx,dword ptr [rsp+118h]
00007FF7704D18F8 lea rcx,[__E4547C95_file1@c (07FF7704E1003h)]
00007FF7704D18FF call __CheckForDebuggerJustMyCode (07FF7704D1082h)
switch (a)
00007FF7704D1904 mov eax,dword ptr [a]
00007FF7704D190A mov dword ptr [rbp+0C0h],eax
00007FF7704D1910 cmp dword ptr [rbp+0C0h],1
00007FF7704D1917 je function+54h (07FF7704D1924h)
00007FF7704D1919 cmp dword ptr [rbp+0C0h],2
00007FF7704D1920 je function+62h (07FF7704D1932h)
00007FF7704D1922 jmp function+70h (07FF7704D1940h)
{
case 1:
printf("1");
00007FF7704D1924 lea rcx,[string "1" (07FF7704D9C10h)]
00007FF7704D192B call printf (07FF7704D11D1h)
break;
00007FF7704D1930 jmp function+7Ch (07FF7704D194Ch)
case 2:
printf("2");
00007FF7704D1932 lea rcx,[string "2" (07FF7704D9C14h)]
00007FF7704D1939 call printf (07FF7704D11D1h)
break;
00007FF7704D193E jmp function+7Ch (07FF7704D194Ch)
default:
printf("3");
00007FF7704D1940 lea rcx,[string "3" (07FF7704D9C18h)]
00007FF7704D1947 call printf (07FF7704D11D1h)
break;
}
}
汇编分析:
情况一总结:
当switch当中case分支较少的时候,其对应的汇编程序和if else 语句当中所对应的汇编程序语言和逻辑基本相同。
先获到要比较的目标参数值对象,然够将该参数值对象存放到存放外部参数的栈内存空间当中去。
从栈内存空间当中获取到目标参数值对象,以此与case分支语句当中的条件值进行对比,当二者相同的时候,则直接跳转到对应case分支语句所在的内存空间位置处,否则继续和下一个case分支语句进行比较。
当所比较的所有case分支语句都不符合的时候,直接跳转到default所对应的分支语句当中去。
情况二:
当switch当中的case语句有多条且有序的时候,其对应的汇编程序分析:
#include <stdlib.h>
#include<stdio.h>
void function(int a){
switch (a)
{
case 1:
printf("1");
break;
case 2:
printf("2");
break;
case 3:
printf("3");
break;
case 4:
printf("4");
break;
case 5:
printf("5");
break;
default:
printf("error");
break;
}
}
int main(int argc, char const *argv[])
{
function(3);
return 0;
}
汇编分析:
情景二总结:当switch case语句当中多条case连续的时候将会进行以下执行方式:
根据当前接收到的参数值a-1(case语句当中第一个值)来计算出偏移量
判断当前偏移量是否大于default语句当中的偏移量,如果大于则直接跳转到default语句对应处进行执行。
当偏移量小于default语句处所对应的偏移量的时候,通过计算查找记录所有case分支语句对应的内存位置的数据表来跳转到当前偏移量所对应的case分支语句所在的位置。
情景三:
当在连续的case语句当中删除某一条语句之后对其对应的汇编进行分析。
程序依然为情景二当中的程序,不同的是将case3条件分支语句进行删除之后进行执行。对应的汇编如图所示:
情景三总结:
当在多个连续的case分支语句当中删除某几个case分支语句之后,被删除的case分支语句所对应的大表当中所对应的程序内存的跳转地址将会被default分支语句所在的内存地址进行替代。
情景四:
当在switch case语句当中删除多个连续的case语句之后,其存储方式将会发生改变。因为如果继续采用对删除的case分支语句当中大表对应处地址用default分支来进行替代的话,则会浪费大量的内存空间。
#include <stdlib.h>
#include<stdio.h>
void function(int a){
switch (a)
{
case 1:
printf("1");
break;
case 9:
printf("9");
break;
case 10:
printf("10");
break;
case 11:
printf("11");
break;
case 12:
printf("12");
break;
default:
printf("error");
break;
}
}
int main(int argc, char const *argv[])
{
function(12);
return 0;
}
汇编分析:
根据小表当中的值在对大表进行二次查询:
情景四总结:
当switch case语句当中有多个连续的case分支语句被省略之后,编译器将会改变数据的存放方式不在继续在大表当中用default分支语句所对应的内存地址来替代被省略的分支语句。
首先获取参数值并计算偏移量和default分支编译量进行比较,当大于default分支的偏移量的时候,则直接跳转到default分支语句当中进行执行操作。
当小于default分支语句的时候,对小表进行数据的查询,因为在小表当中存放了所有case分支语句所对应的一级偏移量(包括省略的case分支语句),并且由于小表当中的每个分支所对应的一级偏移量只有一个字节,而大表当中的二级偏移量则为4个字节,所以当有多个省略的case分支的时候,能够有效的节约内存空间。
当找到小表当中对应的一级偏移量之后,然后到大表当中进行查询可以获取到二级偏移量,也就是当前偏移所对应的case语句所在的内存空间的位置。