汇编的角度分析C语言的switch语句

1、Switch语句与if  else的关系:正向开发时,switch 几乎就是If else语句的另一种表达方式。

 

if(表达式 == 常量1)            
{            
    //...代码        
}            
else if(表达式 == 常量2)            
{            
    //...代码        
}            
else if(表达式 == 常量3)            
{            
    //...代码        
}            
else            
{            
    //...代码        
}            
            
            
switch(表达式)        
    {        
             case 常量表达式1:    
            语句;
            break;
        case 常量表达式2:    
            语句;
            break;
        case 常量表达式3:    
            语句;
            break;
        case 常量表达式3:    
            语句;
            break;
        default:    
            语句;
            break;
    }        

 

 

  switch要求:

  a、case后面必须是常量表达式

  b、case后常量表达式的值不能一样

  c、switch后面表达式必须为整数

 

 

 

2、Switch中,case分支语句小于等于3时候,编译器在执行时候与if else一模一样,大于3的时候。开始生成大表结构

  源码:

void fun(int x){            
    switch(x)            
    {            
        case 1:        
            printf("%d",1);    
            break;    
        case 2:        
            printf("%d",2);    
            break;    
        case 3:        
            printf("%d",3);        
            break;    
        /*case 4:            
            printf("%d",4);    
            break;    
        case 5:    
            printf("%d",5);    
            break;
        case 6:    
            printf("%d",6);    
            break;
            */
        default:        
            printf("%s","default");
            break;    
    }            

}

int main(int argc, char* argv[])
{
    fun(3);
    return 0;
}

 

  反汇编:

00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,44h
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-44h]
0040102C   mov         ecx,11h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]
00401038   mov         eax,dword ptr [ebp+8]
0040103B   mov         dword ptr [ebp-4],eax
0040103E   cmp         dword ptr [ebp-4],1
00401042   je          fun+32h (00401052) 
00401044   cmp         dword ptr [ebp-4],2
00401048   je          fun+43h (00401063)                     相等则跳转,分支跳转语句,和if else跳转几乎一致
0040104A   cmp         dword ptr [ebp-4],3
0040104E   je          fun+54h (00401074) 
00401050   jmp         fun+65h (00401085) 
00401052   push        1
00401054   push        offset string "%d" (0042202c)
00401059   call        printf (00401180)
0040105E   add         esp,8
00401061   jmp         fun+77h (00401097)
00401063   push        2
00401065   push        offset string "%d" (0042202c)
0040106A   call        printf (00401180)
0040106F   add         esp,8
00401072   jmp         fun+77h (00401097)
00401074   push        3
00401076   push        offset string "%d" (0042202c)
0040107B   call        printf (00401180)
00401080   add         esp,8
00401083   jmp         fun+77h (00401097)
00401085   push        offset string "default" (00422020)
0040108A   push        offset string "%s" (0042201c)
0040108F   call        printf (00401180)
00401094   add         esp,8
00401097   pop         edi
00401098   pop         esi
00401099   pop         ebx
0040109A   add         esp,44h
0040109D   cmp         ebp,esp
0040109F   call        __chkesp (00401200)
004010A4   mov         esp,ebp
004010A6   pop         ebp
004010A7   ret

分支语句大于3的

源码:

void fun(int x){            
    switch(x)            
    {            
        case 1:        
            printf("%d",1);    
            break;    
        case 2:        
            printf("%d",2);    
            break;    
        case 3:        
            printf("%d",3);        
            break;    
        case 4:            
            printf("%d",4);    
            break;    
        case 5:    
            printf("%d",5);    
            break;
        case 6:    
            printf("%d",6);    
            break;
            
        default:        
            printf("%s","default");
            break;    
    }            

}

int main(int argc, char* argv[])
{
    fun(3);
    return 0;
}

 

反汇编:

00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,44h
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-44h]
0040102C   mov         ecx,11h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]
00401038   mov         eax,dword ptr [ebp+8]
0040103B   mov         dword ptr [ebp-4],eax
0040103E   mov         ecx,dword ptr [ebp-4]           传入的参数 -1  放入第一个局部变量中    3-1=2   
00401041 sub ecx,1 00401044 mov dword ptr [ebp-4],ecx
00401047 cmp dword ptr [ebp-4],5 0040104B ja $L539+11h (004010bd) 跳入default 0040104D mov edx,dword ptr [ebp-4] 00401050 jmp dword ptr [edx*4+4010E0h] 查表直接跳转到分支语句,因此它的效率是最高的 $L533: 00401057 push 1 00401059 push offset string "%d" (0042202c) 0040105E call printf (00401180) 00401063 add esp,8 00401066 jmp $L539+23h (004010cf) $L535: 00401068 push 2 0040106A push offset string "%d" (0042202c) 0040106F call printf (00401180) 00401074 add esp,8 00401077 jmp $L539+23h (004010cf) $L536: 00401079 push 3 0040107B push offset string "%d" (0042202c) 00401080 call printf (00401180) 00401085 add esp,8 00401088 jmp $L539+23h (004010cf) $L537: 0040108A push 4 0040108C push offset string "%d" (0042202c) 00401091 call printf (00401180) 00401096 add esp,8 00401099 jmp $L539+23h (004010cf) $L538: 0040109B push 5 0040109D push offset string "%d" (0042202c) 004010A2 call printf (00401180) 004010A7 add esp,8 004010AA jmp $L539+23h (004010cf) $L539: 004010AC push 6 004010AE push offset string "%d" (0042202c) 004010B3 call printf (00401180) 004010B8 add esp,8 004010BB jmp $L539+23h (004010cf) 004010BD push offset string "default" (00422020) 004010C2 push offset string "%s" (0042201c) 004010C7 call printf (00401180) 004010CC add esp,8 004010CF pop edi 004010D0 pop esi 004010D1 pop ebx 004010D2 add esp,44h 004010D5 cmp ebp,esp 004010D7 call __chkesp (00401200) 004010DC mov esp,ebp 004010DE pop ebp 004010DF ret

   总结:

  生成大表直接查表的跳转,分支语句需要大于3,

  生成的大表

 

3、既生成大表也生成小表的情况

  源码:

 

void fun(int x){            
    switch(x)            
    {            
        case 1:        
            printf("%d",1);    
            
        case 8:    
            printf("%d",8);    
            break;
        case 9:    
            printf("%d",9);    
            break;
        case 10:    
            printf("%d",10);    
            break;


            
        default:        
            printf("%s","default");
            break;    
    }            

}

int main(int argc, char* argv[])
{
    fun(9);
    return 0;
}

 

 

 

  反汇编:

 

00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,44h
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-44h]
0040102C   mov         ecx,11h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]
00401038   mov         eax,dword ptr [ebp+8]
0040103B   mov         dword ptr [ebp-4],eax
0040103E   mov         ecx,dword ptr [ebp-4]
00401041   sub         ecx,1
00401044   mov         dword ptr [ebp-4],ecx
00401047   cmp         dword ptr [ebp-4],9
0040104B   ja          $L537+11h (004010a1)
0040104D   mov         eax,dword ptr [ebp-4]
00401050   xor         edx,edx
00401052   mov         dl,byte ptr  (004010d8)[eax]                  地址  004010d8+eax处的值
00401058   jmp         dword ptr [edx*4+4010C4h]          4010c4  大表的地址,下边跟着很多小表的地址
$L533:
0040105F   push        1
00401061   push        offset string "%d" (0042202c)
00401066   call        printf (004011b0)
0040106B   add         esp,8
$L535:
0040106E   push        8
00401070   push        offset string "%d" (0042202c)
00401075   call        printf (004011b0)
0040107A   add         esp,8
0040107D   jmp         $L537+23h (004010b3)
$L536:
0040107F   push        9
00401081   push        offset string "%d" (0042202c)
00401086   call        printf (004011b0)
0040108B   add         esp,8
0040108E   jmp         $L537+23h (004010b3)
$L537:
00401090   push        0Ah
00401092   push        offset string "%d" (0042202c)
00401097   call        printf (004011b0)
0040109C   add         esp,8
0040109F   jmp         $L537+23h (004010b3)
004010A1   push        offset string "default" (00422020)
004010A6   push        offset string "%s" (0042201c)
004010AB   call        printf (004011b0)
004010B0   add         esp,8
004010B3   pop         edi
004010B4   pop         esi
004010B5   pop         ebx
004010B6   add         esp,44h
004010B9   cmp         ebp,esp
004010BB   call        __chkesp (00401230)
004010C0   mov         esp,ebp
004010C2   pop         ebp
004010C3   ret

 

  总结:大表是连续的地址储存的是分支跳转的地址

     中间隔开的值比较小的话,大表填充的是default的地址

     中间隔开比较大的时候,会生成小表,查小表来跳转

 

posted @ 2021-01-03 21:30  heyhx  阅读(435)  评论(0编辑  收藏  举报