一:两种语句的区别
在讨论if与switch这两种语句的区别时,应该从它们最基础的层面开始。所以我们应该从它们的汇编实现开始探讨。而且应注意到它们都是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语句的效率。