1.switch...case结构的汇编表示

写入switch...case结构的代码:

int fun(char c)
   {
   char res;
   switch(c)
   {
   case 'a':
       res='a';
       break;
   case 'e':
      res='e';
      break;
  case 'i':
      res='i';
      break;
  case 'o':
      res='o';
      break;
  case 'u':
      res='u';
      break;
  default:
      res=' ';
  }

用gcc产生的汇编代码是:

00000000 <fun>:
   0:	55                   	push   %ebp
   1:	89 e5                	mov    %esp,%ebp
   3:	83 ec 14             	sub    $0x14,%esp
   6:	8b 45 08             	mov    0x8(%ebp),%eax
   9:	88 45 ec             	mov    %al,-0x14(%ebp)
   c:	0f be 45 ec          	movsbl -0x14(%ebp),%eax
  10:	83 e8 61             	sub    $0x61,%eax
  13:	83 f8 14             	cmp    $0x14,%eax
  16:	77 27                	ja     3f <fun+0x3f>
  18:	8b 04 85 00 00 00 00 	mov    0x0(,%eax,4),%eax
  1f:	ff e0                	jmp    *%eax
  21:	c6 45 ff 61          	movb   $0x61,-0x1(%ebp)
  25:	eb 1c                	jmp    43 <fun+0x43>
  27:	c6 45 ff 65          	movb   $0x65,-0x1(%ebp)
  2b:	eb 16                	jmp    43 <fun+0x43>
  2d:	c6 45 ff 69          	movb   $0x69,-0x1(%ebp)
  31:	eb 10                	jmp    43 <fun+0x43>
  33:	c6 45 ff 6f          	movb   $0x6f,-0x1(%ebp)
  37:	eb 0a                	jmp    43 <fun+0x43>
  39:	c6 45 ff 75          	movb   $0x75,-0x1(%ebp)
  3d:	eb 04                	jmp    43 <fun+0x43>
  3f:	c6 45 ff 20          	movb   $0x20,-0x1(%ebp)
  43:	0f be 45 ff          	movsbl -0x1(%ebp),%eax
  47:	c9                   	leave  
  48:	c3                   	ret

计算机中处理switch...case结构时,会生成跳转表,根据变量的取值跳转到合适的分支。在上面的例子中,变量c存放在ebp偏移8字节的位置,它首先减去97,如果大于20则跳转到默认分支。在执行完对应的分支后,每个分支后都有jmp43语句用于跳转到函数的结尾。

2.if...else语句的汇编表示

上面的代码写成if...else的形式,产生的汇编代码是:

00000000 <fun>:
   0:	55                   	push   %ebp
   1:	89 e5                	mov    %esp,%ebp
   3:	83 ec 14             	sub    $0x14,%esp
   6:	8b 45 08             	mov    0x8(%ebp),%eax
   9:	88 45 ec             	mov    %al,-0x14(%ebp)
   c:	80 7d ec 61          	cmpb   $0x61,-0x14(%ebp)
  10:	75 06                	jne    18 <fun+0x18>
  12:	c6 45 ff 61          	movb   $0x61,-0x1(%ebp)
  16:	eb 34                	jmp    4c <fun+0x4c>
  18:	80 7d ec 65          	cmpb   $0x65,-0x14(%ebp)
  1c:	75 06                	jne    24 <fun+0x24>
  1e:	c6 45 ff 65          	movb   $0x65,-0x1(%ebp)
  22:	eb 28                	jmp    4c <fun+0x4c>
  24:	80 7d ec 69          	cmpb   $0x69,-0x14(%ebp)
  28:	75 06                	jne    30 <fun+0x30>
  2a:	c6 45 ff 69          	movb   $0x69,-0x1(%ebp)
  2e:	eb 1c                	jmp    4c <fun+0x4c>
  30:	80 7d ec 6f          	cmpb   $0x6f,-0x14(%ebp)
  34:	75 06                	jne    3c <fun+0x3c>
  36:	c6 45 ff 6f          	movb   $0x6f,-0x1(%ebp)
  3a:	eb 10                	jmp    4c <fun+0x4c>
  3c:	80 7d ec 75          	cmpb   $0x75,-0x14(%ebp)
  40:	75 06                	jne    48 <fun+0x48>
  42:	c6 45 ff 75          	movb   $0x75,-0x1(%ebp)
  46:	eb 04                	jmp    4c <fun+0x4c>
  48:	c6 45 ff 20          	movb   $0x20,-0x1(%ebp)
  4c:	0f be 45 ff          	movsbl -0x1(%ebp),%eax
  50:	c9                   	leave  
  51:	c3                   	ret

可见,如果输入了字符a,这个程序将不需要跳转,这时if...else的效率要高于switch...case.如果是输入了字符o或者u,程序将多次跳转,程序的分支变多时,必然会导致效率降低。

3.switch...case语句和if...else效率比较

这里不考虑编译器的优化和条件传送(条件传送介绍),仅仅从跳转次数来比较两者的效率。

switch...case结构中有跳转表,输入的字符只要经过一次比较就可以正确的找到跳转分支,所以平均情况下跳转次数为1.

if...else结构如果有n个分支,分别记为n0,n1,n2,n3,...n(i-1),每个分支出现的概率假设未pi,分别为p0,p1,p2,p3,...p(i-1)。执行第一分支前不需要跳转,其它分支均需要跳转,执行第二个分支之前需要跳转一次,第三个分支需要跳转两次...需要跳转平均跳转的次数s为:  

Java switch和if效率分析_switch...case

如果

Java switch和if效率分析_switch...case

>1,则此时if...case的效率是小于switch...case的,如果它的值小于1,此时if...else的效率高于switch...case。    假设if...else分支的每个分支出现的概率相同,即1/n,上面的跳转次数的期望值可以改写为:

Java switch和if效率分析_switch...case_03


如果n=3,则期望的跳转次数为1,刚好与switch...case相同。由此,如果选择分支大于3的时候,选用switch...case结构效率会更高一些,而小于3时,选用if...else结构更好。

总结:在选择分支较多时,选用switch...case结构会提高程序的效率,但switch不足的地方在于只能处理字符或者数字类型的变量,if...else结构更加灵活一些,if...else结构可以用于判断表达式是否成立,比如if(a+b>c),if...else的应用范围更广,switch...case结构在某些情况下可以替代if...else结构。