一、事情來源
事情來源是一段奇怪的代碼,代碼如下
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.