一、事情来源
事情来源是一段奇怪的代码,代码如下
int x = 1000;
switch (x) {
case 1000:
{
NSLog(@"%d", 1);
}
case 2000:
{
NSLog(@"%d", 2);
}
break;
case 3:
NSLog(@"%d", 3);
break;
default:
NSLog(@"%d", -1);
case 4:
NSLog(@"%d", 4);
break;
case 5:
NSLog(@"%d", 5);
break;
}
当 x = 1000的时候,代码输出的是 1和2 ,也就是 1000 和 2000的case都执行了。(测试环境是Xcode + Mac iphone 模拟器)
原因是什么?为什么不是和if else if else一样呢
根据网上的资料,VC6.0的编译器在case数量小于3个的时候,会使用类似if else if else的语句,
也就是这个case不加break语句的时候,进入下一条case的时候依然需要判断条件。
当case数量较多的时候,编译器为了优化性能,会产生一个表,表的地址就是case的汇编入口,每个case完了之后下面接着是一个break语句,jump到switch case结束的地方
如果你的case忘记了break语句,那么很可能继续执行到下一个case,因为所有case的指令都是平铺的,知道运行到break
那么在iOS的设备上,不管你是几条指令,编译器都会使用跳转表的方式实现。
比如下面的代码的汇编指令:
int x = 1;
switch (x) {
case 1:
{
NSLog(@"%d", 1);
}
case 2:
{
NSLog(@"%d", 2);
}
case 3:
NSLog(@"%d", 3);
case 4:
NSLog(@"%d", 4);
break;
case 5:
NSLog(@"%d", 5);
break;
default:
NSLog(@"%d", -1);
}
对应汇编
0x10da4361a <+58>: movl -0x24(%rbp), %eax
0x10da4361d <+61>: decl %eax
0x10da4361f <+63>: movl %eax, %esi
0x10da43621 <+65>: subl $0x4, %eax
0x10da43624 <+68>: movq %rsi, -0x30(%rbp)
0x10da43628 <+72>: movl %eax, -0x34(%rbp)
0x10da4362b <+75>: ja 0x10da436bd ; <+221> at ViewController.m
0x10da43631 <+81>: leaq 0xa4(%rip), %rax ; -[ViewController viewDidLoad] + 252
0x10da43638 <+88>: movq -0x30(%rbp), %rcx
0x10da4363c <+92>: movslq (%rax,%rcx,4), %rdx
0x10da43640 <+96>: addq %rax, %rdx
0x10da43643 <+99>: jmpq *%rdx
0x10da43645 <+101>: leaq 0x1a14(%rip), %rax ; @"%d"
0x10da4364c <+108>: movl $0x1, %esi
0x10da43651 <+113>: movq %rax, %rdi
0x10da43654 <+116>: movb $0x0, %al
0x10da43656 <+118>: callq 0x10da43a14 ; symbol stub for: NSLog
0x10da4365b <+123>: leaq 0x19fe(%rip), %rax ; @"%d"
0x10da43662 <+130>: movl $0x2, %esi
0x10da43667 <+135>: movq %rax, %rdi
0x10da4366a <+138>: movb $0x0, %al
0x10da4366c <+140>: callq 0x10da43a14 ; symbol stub for: NSLog
0x10da43671 <+145>: leaq 0x19e8(%rip), %rax ; @"%d"
0x10da43678 <+152>: movl $0x3, %esi
0x10da4367d <+157>: movq %rax, %rdi
0x10da43680 <+160>: movb $0x0, %al
0x10da43682 <+162>: callq 0x10da43a14 ; symbol stub for: NSLog
0x10da43687 <+167>: leaq 0x19d2(%rip), %rax ; @"%d"
0x10da4368e <+174>: movl $0x4, %esi
0x10da43693 <+179>: movq %rax, %rdi
0x10da43696 <+182>: movb $0x0, %al
0x10da43698 <+184>: callq 0x10da43a14 ; symbol stub for: NSLog
0x10da4369d <+189>: jmp 0x10da436d3 ; <+243> at ViewController.m:66
0x10da436a2 <+194>: leaq 0x19b7(%rip), %rax ; @"%d"
0x10da436a9 <+201>: movl $0x5, %esi
0x10da436ae <+206>: movq %rax, %rdi
0x10da436b1 <+209>: movb $0x0, %al
0x10da436b3 <+211>: callq 0x10da43a14 ; symbol stub for: NSLog
0x10da436b8 <+216>: jmp 0x10da436d3 ; <+243> at ViewController.m:66
0x10da436bd <+221>: leaq 0x199c(%rip), %rax ; @"%d"
0x10da436c4 <+228>: movl $0xffffffff, %esi ; imm = 0xFFFFFFFF
0x10da436c9 <+233>: movq %rax, %rdi
0x10da436cc <+236>: movb $0x0, %al
0x10da436ce <+238>: callq 0x10da43a14 ; symbol stub for: NSLog
可以看到输出1,2,3 的case下面都没jmp指令;case 4的时候,会出现jump指令,也就是上面的代码是采用跳转表进行优化的。
回到最开始的代码,如果如数的x = 30的时候,会输出多少呢?
int x = 1000;
switch (x) {
case 1000:
{
NSLog(@"%d", 1);
}
case 2000:
{
NSLog(@"%d", 2);
}
break;
case 3:
NSLog(@"%d", 3);
break;
default:
NSLog(@"%d", -1);
case 4:
NSLog(@"%d", 4);
break;
case 5:
NSLog(@"%d", 5);
break;
}
会走到default分支,输出-1;然后走到下面4的case,输出4.