switch case 跳转表
一、事情来源
事情来源是一段奇怪的代码,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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的设备上,不管你是几条指令,编译器都会使用跳转表的方式实现。
比如下面的代码的汇编指令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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); } |
对应汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | 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的时候,会输出多少呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架