VS_C_17/11/22 C中if与switch的区别

来源:互联网 发布:群星修改数据 编辑:程序博客网 时间:2024/06/06 08:28

一:两种语句的区别

在讨论ifswitch这两种语句的区别时,应该从它们最基础的层面开始。所以我们应该从它们的汇编实现开始探讨。而且应注意到它们都是C中最常用的选择语句,既然是选择语句就还得从它们的实现效率上讨论一番。

先看switch语句:

1,switch...case会生成一个跳转表来指示实际的case分支的地址,而这个跳转表的索引号与switch变量的值是相的。从而,switch...case,只需访问对应索引号的表项从而到达定位分支的目的。
switch...case会生成一份大小(表项数)为最大case常量+1查询,程序首先判断switch变量是否大于最大case常量,若大于,则跳到default分支处理;否则取得索引号为switch变量大小的查询表项的地址(查询表的起始地址+表项大小*索引号),程序接着跳到此地址执行

我们可以用VS来看一下:


switch-case在实现时当case项比较多时,会通过生成查询表来提高程序的效率,空间换时间。

而这个界限一般是4(所以我们用了4个选择)大于4个选项时,switch就会生成一个查询表,并且在执行switch以下语句(case)时已经查询了一次,在执行case查找常量时只需要对应表格个常量位置执行其对应的语句既可。

我们来看一下这段代码的汇编语言:

switch (i)

00F913E5  mov     eax,dword ptr [i]  

00F913E8  mov     dword ptr [ebp-0D0h],eax  

00F913EE  cmp     dword ptr [ebp-0D0h],4  //cmp 在汇编中一条查询指令

00F913F5  ja      $LN6+2Dh (0F91431h)  //ja指令用于无符号数比较,取jump if above,及前者大于后者则转移

00F913F7  mov     ecx,dword ptr [ebp-0D0h]  

00F913FD  jmp     dword ptr [ecx*4+0F91468h]  //查询表的起始地址+(输入值 - 1)* 索引号

case 0: 中括号在汇编中类似于C中的指针,即取地址的意思。这句执行后就将中括号中算出的

j = 0; 地址中存的操作数取出,这个操作数就是case下的地址00F9141F(可验证)

00F91404  mov     dword ptr [j],0  

break;

00F9140B  jmp     $LN6+34h (0F91438h)  // jmp在汇编中为无条件跳转

case 1:

j = 1;

00F9140D  mov     dword ptr [j],1  

break;

00F91414  jmp     $LN6+34h (0F91438h)  

case 2:

j = 2;

00F91416  mov     dword ptr [j],2  

break;

00F9141D  jmp     $LN6+34h (0F91438h)  

case 3:

j = 3;

00F9141F  mov     dword ptr [j],3  

break;

00F91426  jmp     $LN6+34h (0F91438h)  

case 4:

j = 4;

00F91428  mov     dword ptr [j],4  

break;

00F9142F  jmp     $LN6+34h (0F91438h)  

default:

j = 10;

00F91431  mov     dword ptr [j],0Ah  

break;

}

printf("%d\n", j);

0F91438h  mov         esi,esp

这段汇编语言中,可以看到上面红色标出的,在switch的汇编语言中,在达到4个的选择时,只会进行一次查询,而且是在case指令之前就已执行,这也应证了前面对查询表的解释。

JMP 在汇编中表示无条件跳转,可以看到它出现了6次,代表代码中有6次跳转。

可以看到在每个case之后都有一个蓝色标注的地址,这其实就是前面JMP跳转到的地址,这个地址其实就是printf之后的一个地址,意思是在switch之后如果 i 与case之后的立即数相同就跳转到printf打印。

2,下来我们来看if语句:


我同样对if语句做了4个及以上的选择。而且我们用5来实验它的查询效率。

要知道if语句的实现基础还是需要汇编代码,,其采用遍历查询的方式。我们根据上面的思路只需要知道if语句的代码中cmp(查询)的个数:

一:  if (j == 0)

         010113EE  cmp        dword ptr [j],0

         010113F2 jne         lbf1+3Fh (01011429h)   //jne  有条件跳转

二:  if (j == 1)         

         01011429  cmp         dword ptr [j],1

三:  if (j == 2)                   

         01011430  cmp         dword ptr [j],2  

         01011434  jne         lbf1+81h (01011451h)

四:  if (j == 3)

         01011451 cmp         dword ptr [j],3  

         01011455  jne         lbf1+0A2h (01011472h)

五:  if (j == 4)

         01011472  cmp        dword ptr [j],4  

         01011476  jne         lbf1+0C3h (01011493h)

六:  if (j == 5)

         01011493  cmp         dword ptr [j],5  

         01011497  jne         lbf1+0E4h (010114B4h)

可以看出在条件为5时需要查询6次即每个条件都查询了一次,效率显然没有switch高,在语句中需要遍历条件分支直到命中条件,这也说明在写if语句的时候需要把可能性大的条件放在前面。

在if的汇编中也有类似于上面switch中的JMP跳转,但这里是jne(有条件跳转)。意思是在if进行判断时,如果为真就执行if内部语句块,如果为假则有条件跳转到下一个if语句,直到判断为真执行语句块。

二:两种语句的共性

两者虽有差距,但它们的基本原理相似,都是与C中的立即数相比较,相等时打印并跳转返回。而且经过比较,switch语句在选择数小于4时也会执行超过一个cmp指令。

就有点类似于if语句了。

三:总结语句的特点

1,当分支较多时,使用switch语句的效率比if语句高很多。

2,switch语句其实是一种空间换时间的方法,在case常量分布范围很大但实际有效值又比较少的情况switch的效果并不是很好。而且if语句有较大的灵活性,switch只能查询常量。

3,switch只能处理case为常量的情况,其灵活性相比于if语句差很多。

4,在使用if语句时将可能性最大的条件放在前面可提高if语句的效率。

 













原创粉丝点击