switch和if else的比较,从底层解释

在网上查了一些资料说什么,三个判断以下选择if语句,三个判断以上选择switch语句,我查了一些资料都没有怎么说明这是为什么,所以今天从汇编角度对其进行了分析。

分析

if语句

C代码

int main()
{
  int i = 10;
  if(i==1)
  {
    printf("1");
  }else if(i==2)
  {
    printf("2");
  }else if(i==3)
  {
    printf("3");
  }
}

汇编代码

00E12165  mov         dword ptr [ebp-8],0Ah  //存储局部变量 10
00E1216C  cmp         dword ptr [ebp-8],1    //比较 1跟10进行比较
00E12170  jne         00E12181               //不相等就跳转到 00E12181 地址(下一个if判断)
00E12172  push        0E17B94h               
00E12177  call        00E110D2               //调用printf函数
00E1217C  add         esp,4  
00E1217F  jmp         00E121A9   
00E12181  cmp         dword ptr [ebp-8],2    //比较是否等于2
00E12185  jne         00E12196               //不等于就进行跳转到 00E12196 地址(下一一个if判断)
00E12187  push        0E17B98h  
00E1218C  call        00E110D2                //调用printf函数
00E12191  add         esp,4  
00E12194  jmp         00E121A9  
00E12196  cmp         dword ptr [ebp-8],3     //判断是否等于3
00E1219A  jne         00E121A9                //不等于进行跳转
00E1219C  push        0E17B9Ch  
00E121A1  call        00E110D2                //调用printf函数
00E121A6  add         esp,4  
00E121A9  pop         edi

观察以上汇编指令可以看出,if 语句就是一个一个的判断跳转。

switch语句

C代码

int main()
{
	int i = 10;
	switch (i)
	{
	case 1:
		printf("1");
		break;
	case 2:
		printf("2");
		break;
	case 3:
		printf("3");
		break;
	default:
		printf("error");
		break;
	}
}

汇编代码

009E2165  mov         dword ptr [ebp-8],0Ah  		//保存局部变量 10
009E216C  mov         eax,dword ptr [ebp-8]  
009E216F  mov         dword ptr [ebp+FFFFFF30h],eax  
009E2175  cmp         dword ptr [ebp+FFFFFF30h],1  	// 1 跟 10 进行比较
009E217C  je          009E2192  			        //相等就进行跳转
009E217E  cmp         dword ptr [ebp+FFFFFF30h],2  	//2 跟 10 进行比较
009E2185  je          009E21A1  			       //相等进行跳转
009E2187  cmp         dword ptr [ebp+FFFFFF30h],3  	//3 跟 10 进行比较
009E218E  je          009E21B0  			       //相等进行跳转
009E2190  jmp         009E21BF  			      //都不相等进入default
009E2192  push        9E7B94h  
009E2197  call        009E10D2  
009E219C  add         esp,4  
009E219F  jmp         009E21CC  
009E21A1  push        9E7B98h  
009E21A6  call        009E10D2  
009E21AB  add         esp,4  
009E21AE  jmp         009E21CC  
009E21B0  push        9E7B9Ch  
009E21B5  call        009E10D2  
009E21BA  add         esp,4  
009E21BD  jmp         009E21CC  
009E21BF  push        9E7BA8h  
009E21C4  call        009E10D2  
009E21C9  add         esp,4  
009E21CC  pop         edi  

分析

通过分析 if 和 switch的反汇编代码,发现都是通过一级一级的判断来实现的判断的,除了生成的语法不同其他流程都是一样的,那么不就说明了两个随便怎么使用都是一样的了吗?
其实是不一样的,编译器会通过不同的 switch 生成不同的汇编代码,下面继续分析 switch 反汇编代码。

4个分支switch语句

4个分支以上的 switch 分支语句,编译器将会对汇编生成规则进行优化了(不用记住 4个分支 这个值,因为不同编译器不一定一样)。
C代码

int main()
{
	int i = 10;
	switch (i)
	{
	case 1:
		printf("1");
		break;
	case 2:
		printf("2");
		break;
	case 3:
		printf("3");
		break;
	case 4:
		printf("4");
	default:
		printf("error");
		break;
	}
}

汇编代码

00672125  mov         dword ptr [ebp-8],0Ah  		//保存局部变量 10 
0067212C  mov         eax,dword ptr [ebp-8]  
0067212F  mov         dword ptr [ebp+FFFFFF30h],eax  
00672135  mov         ecx,dword ptr [ebp+FFFFFF30h]  
0067213B  sub         ecx,1  
0067213E  mov         dword ptr [ebp+FFFFFF30h],ecx  
00672144  cmp         dword ptr [ebp+FFFFFF30h],3  	//判断是否是默认default
0067214B  ja          00672194  			//如果是default,就跳转过去
0067214D  mov         edx,dword ptr [ebp+FFFFFF30h] 	//获取分支下标
00672153  jmp         dword ptr [edx*4+006721B8h]  	//计算分支地址(这里计算出来的值就是 switch 分支地址表地址),然后跳转到对应地址。
0067215A  push        677B94h  
0067215F  call        006710D2  
00672164  add         esp,4  
00672167  jmp         006721A1  
00672169  push        677B98h  
0067216E  call        006710D2  
00672173  add         esp,4  
00672176  jmp         006721A1  
00672178  push        677B9Ch  
0067217D  call        006710D2  
00672182  add         esp,4  
00672185  jmp         006721A1  
00672187  push        677BA0h  
0067218C  call        006710D2  
00672191  add         esp,4  
00672194  push        677BA8h  
00672199  call        006710D2  
0067219E  add         esp,4  
006721A1  pop         edi  

switch分支地址表

0x006721B8  5a 21 67 00  Z!g.
0x006721BC  69 21 67 00  i!g.
0x006721C0  78 21 67 00  x!g.
0x006721C4  87 21 67 00  ?!g.

通过观察上面的汇编可以得出,在 4 个分支的情况下 switch 生成的汇编代码就发生了变化了,减少了很多判断语句,而是通过查表得方式来查找具体得执行地址。
总结流程

  1. 判断是否默认分支,如果是就跳转到默认分支。
  2. 如果不是默认分支,通过计算在地址表中查询分支地址。
  3. 跳转到查询到得分支地址,执行分支代码。

总结

分支少于 3 个的情况下if跟switch生成的判断是一样的(不同编译器可能会不一样)。
分支多于 3 个的情况switch是通过查表得方式,来实现查询分支地址,减少了很多判断从而提高执行性能。

注意

上面只说明了正常情况下的switch生成汇编代码的情况,在其他情况也会生成其他格式的汇编代码,而且效率不一定比if语句执行效率高,比如case是1,2,3,10,50,500这种分支,它生成的汇编就和if语句是一样的,所以我们在写switch语句是尽量确保case的连续性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值