1.先看个最简单的switch(小于4)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void func(int a) { switch (a) { case 1: printf("1111"); break; case 2: printf("2222"); break; case 3: printf("3333"); break; default: printf("5555"); break; } } int main(int argc, char * argv[]) { func(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 45 46 |
huibian-switch`func: 0x100b3e834 <+0>: sub sp, sp, #0x40 ; =0x40 #sp拉伸4*16栈空间大小 0x100b3e838 <+4>: stp x29, x30, [sp, #0x30] #x29,x30入栈(sp上取栈地址) 0x100b3e83c <+8>: add x29, sp, #0x30 ; =0x30 #sp上取3*16栈地址位置给x29 0x100b3e840 <+12>: stur w0, [x29, #-0x4] #x29-4空间给w0 -> 0x100b3e844 <+16>: ldur w0, [x29, #-0x4] #把上一步值读到寄存器w0 0x100b3e848 <+20>: mov x8, x0 #x0给x8(x0也就是参数a) 0x100b3e84c <+24>: subs w0, w0, #0x1 ; =0x1 #w0=w0-1 (w0=1-1, w0=0)(改变标记寄存器)如果相等也就是=0条b.eq #mark_a 0x100b3e850 <+28>: stur w8, [x29, #-0x8] #w8入栈x29=x29-8位置 保护 0x100b3e854 <+32>: stur w0, [x29, #-0xc] #w0入栈 保护 #是否相等,是上面 #mark_a处 标记寄存器决定 (上面是w0=1-1=0 也就是相等) 0x100b3e858 <+36>: b.eq 0x100b3e888 ; <+84> at main.m:17 #也就是参数a-1=0吗?如果等跳0x100b3e888 0x100b3e85c <+40>: b 0x100b3e860 ; <+44> at main.m #不等跳0x100b3e860 0x100b3e860 <+44>: ldur w8, [x29, #-0x8] #然后w8下取值 0x100b3e864 <+48>: subs w9, w8, #0x2 ; =0x2 #w9=w8-2(w9=1-2)(这是如果不等于的情况) 0x100b3e868 <+52>: stur w9, [x29, #-0x10] #w9入栈 0x100b3e86c <+56>: b.eq 0x100b3e89c ; <+104> at main.m:20 #判断是否相等和上面一样了... 0x100b3e870 <+60>: b 0x100b3e874 ; <+64> at main.m 0x100b3e874 <+64>: ldur w8, [x29, #-0x8] 0x100b3e878 <+68>: subs w9, w8, #0x3 ; =0x3 0x100b3e87c <+72>: stur w9, [x29, #-0x14] 0x100b3e880 <+76>: b.eq 0x100b3e8b0 ; <+124> at main.m:23 0x100b3e884 <+80>: b 0x100b3e8c4 ; <+144> at main.m:32 0x100b3e888 <+84>: adrp x0, 1 0x100b3e88c <+88>: add x0, x0, #0xf1c ; =0xf1c 0x100b3e890 <+92>: bl 0x100b3ebec ; symbol stub for: printf 0x100b3e894 <+96>: str w0, [sp, #0x18] 0x100b3e898 <+100>: b 0x100b3e8d4 ; <+160> at main.m:35 0x100b3e89c <+104>: adrp x0, 1 0x100b3e8a0 <+108>: add x0, x0, #0xf21 ; =0xf21 0x100b3e8a4 <+112>: bl 0x100b3ebec ; symbol stub for: printf 0x100b3e8a8 <+116>: str w0, [sp, #0x14] 0x100b3e8ac <+120>: b 0x100b3e8d4 ; <+160> at main.m:35 0x100b3e8b0 <+124>: adrp x0, 1 0x100b3e8b4 <+128>: add x0, x0, #0xf26 ; =0xf26 0x100b3e8b8 <+132>: bl 0x100b3ebec ; symbol stub for: printf 0x100b3e8bc <+136>: str w0, [sp, #0x10] 0x100b3e8c0 <+140>: b 0x100b3e8d4 ; <+160> at main.m:35 0x100b3e8c4 <+144>: adrp x0, 1 0x100b3e8c8 <+148>: add x0, x0, #0xf2b ; =0xf2b 0x100b3e8cc <+152>: bl 0x100b3ebec ; symbol stub for: printf 0x100b3e8d0 <+156>: str w0, [sp, #0xc] 0x100b3e8d4 <+160>: ldp x29, x30, [sp, #0x30] 0x100b3e8d8 <+164>: add sp, sp, #0x40 ; =0x40 0x100b3e8dc <+168>: ret |
1 2 3 |
0x100b3e888 <+84>: adrp x0, 1 0x100b3e88c <+88>: add x0, x0, #0xf1c ; =0xf1c 0x100b3e890 <+92>: bl 0x100b3ebec ; symbol stub for: printf |
解释:这个主要是拿到一个静态变量调用printf打印
打印完执行:
1 2 3 4 5 |
0x100b3e898 <+100>: b 0x100b3e8d4 ; <+160> at main.m:35 0x100b3e8d4 <+160>: ldp x29, x30, [sp, #0x30] #取出x29,x30 0x100b3e8d8 <+164>: add sp, sp, #0x40 ; =0x40 #栈平衡 0x100b3e8dc <+168>: ret #调走 结束 |
对于上面也就是拉伸栈空间(这个前面说过,不多说),w0-1判断是否相等,-2是否相等,-3是否相等,如果相等就直接执行printf,然后取出值结束。其实这个小于3个分支,也就是和if else一样
2.多分支switch执行default(大于或等于4)
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 |
void func(int a) { switch (a) { case 1: printf("1111"); break; case 2: printf("2222"); break; case 3: printf("3333"); break; case 4: printf("4444"); break; default: printf("5555"); break; } } int main(int argc, char * argv[]) { func(5); } |
分析:
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
huibian-switch`func: #和上面一样的栈空间拉伸 0x10290681c <+0>: sub sp, sp, #0x40 ; =0x40 0x102906820 <+4>: stp x29, x30, [sp, #0x30] 0x102906824 <+8>: add x29, sp, #0x30 ; =0x30 #w0(参数)入栈,出栈 0x102906828 <+12>: stur w0, [x29, #-0x4] 0x10290682c <+16>: ldur w0, [x29, #-0x4] #w0=wo-1 (w0就是参数a,w0=5-1=4),w8=4 (subs:减后 标记寄存器也会改变) -> 0x102906830 <+20>: subs w0, w0, #0x1 ; =0x1 0x102906834 <+24>: mov x8, x0 #w0=1(4-3=1)入栈(位置x29, #-0x14),w8入栈(位置x29, #-0x10)(subs:减后 标记寄存器也会改变,会影响下面判断结果) #w0=1是>0 这是大于 #w0也就相当于减-4 为什么减4? #mark_a 0x102906838 <+28>: subs w0, w0, #0x3 ; =0x3 0x10290683c <+32>: stur x8, [x29, #-0x10] 0x102906840 <+36>: stur w0, [x29, #-0x14] #是否无符号大于(上面结果是大于直接跳0x1029068b0) 0x102906844 <+40>: b.hi 0x1029068b0 ; <+148> at main.m:29 #不大于 0x102906848 <+44>: adrp x8, 0 0x10290684c <+48>: add x8, x8, #0x8cc ; =0x8cc 0x102906850 <+52>: ldur x9, [x29, #-0x10] 0x102906854 <+56>: ldrsw x10, [x8, x9, lsl #2] 0x102906858 <+60>: add x8, x10, x8 0x10290685c <+64>: br x8 0x102906860 <+68>: adrp x0, 1 0x102906864 <+72>: add x0, x0, #0xf18 ; =0xf18 0x102906868 <+76>: bl 0x102906be8 ; symbol stub for: printf 0x10290686c <+80>: str w0, [sp, #0x18] 0x102906870 <+84>: b 0x1029068c0 ; <+164> at main.m:32 0x102906874 <+88>: adrp x0, 1 0x102906878 <+92>: add x0, x0, #0xf1d ; =0xf1d 0x10290687c <+96>: bl 0x102906be8 ; symbol stub for: printf 0x102906880 <+100>: str w0, [sp, #0x14] 0x102906884 <+104>: b 0x1029068c0 ; <+164> at main.m:32 0x102906888 <+108>: adrp x0, 1 0x10290688c <+112>: add x0, x0, #0xf22 ; =0xf22 0x102906890 <+116>: bl 0x102906be8 ; symbol stub for: printf 0x102906894 <+120>: str w0, [sp, #0x10] 0x102906898 <+124>: b 0x1029068c0 ; <+164> at main.m:32 0x10290689c <+128>: adrp x0, 1 0x1029068a0 <+132>: add x0, x0, #0xf27 ; =0xf27 0x1029068a4 <+136>: bl 0x102906be8 ; symbol stub for: printf 0x1029068a8 <+140>: str w0, [sp, #0xc] 0x1029068ac <+144>: b 0x1029068c0 ; <+164> at main.m:32 #上面大于直接跳这直接输出 (具体怎么看输入结果看下面解释) #mark_b 0x1029068b0 <+148>: adrp x0, 1 0x1029068b4 <+152>: add x0, x0, #0xf2c ; =0xf2c 0x1029068b8 <+156>: bl 0x102906be8 ; symbol stub for: printf 0x1029068bc <+160>: str w0, [sp, #0x8] 0x1029068c0 <+164>: ldp x29, x30, [sp, #0x30] 0x1029068c4 <+168>: add sp, sp, #0x40 ; =0x40 0x1029068c8 <+172>: ret |
tip:判断是否b.hi大于,是根于subs改变了标记寄存器(状态寄存器)cpsr来判断是否大于 (link:标记寄存器 )
问题:在mark_b处看printf是什么?
1 2 |
0x1029068b0 <+148>: adrp x0, 1 0x1029068b4 <+152>: add x0, x0, #0xf2c ; =0xf2c |
- 0x1029068b0低12位清0 >0x102906000
- 0x102906000低12位值+1 >0x102907000
- 0x102907000 +0xf2c >0x102907f2c
然后在lldb执行:
1 2 |
lldb: p (char*)0x102907f2c lldb: (char *) $1 = 0x0000000102dfff2c "5555" |
问题:在mark_a处为什么减4?
先判断是不是default
3.多分支switch(大于或等于4)
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 |
void func(int a) { switch (a) { case 1: printf("1111"); break; case 2: printf("2222"); break; case 3: printf("3333"); break; case 4: printf("4444"); break; case 5: printf("5555"); break; default: printf("default"); break; } } int main(int argc, char * argv[]) { func(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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
huibian-switch`func: #和上面情况一样 拉伸空间 0x104c427fc <+0>: sub sp, sp, #0x40 ; =0x40 0x104c42800 <+4>: stp x29, x30, [sp, #0x30] 0x104c42804 <+8>: add x29, sp, #0x30 ; =0x30 #w0参数入栈,读出 0x104c42808 <+12>: stur w0, [x29, #-0x4] -> 0x104c4280c <+16>: ldur w0, [x29, #-0x4] #w0=w0-1=1 (标记寄存器改变),w8=1 0x104c42810 <+20>: subs w0, w0, #0x1 ; =0x1 0x104c42814 <+24>: mov x8, x0 #w0=1-4 ,w0,x8入栈 #mark-a 0x104c42818 <+28>: subs w0, w0, #0x4 ; =0x4 0x104c4281c <+32>: stur x8, [x29, #-0x10] #mark-1 0x104c42820 <+36>: stur w0, [x29, #-0x14] #判断是否是default, 上面#mark-a处结果是个负数,不是default,执行b.hi下面代码 0x104c42824 <+40>: b.hi 0x104c428a4 ; <+168> at main.m:32 #不是default执行这个 #取值x8=0x104c438c0 主要是取表 #0x104c438c0: 79 22 32 34 00 42 33 32 40 30 3a 38 40 22 55 49 y"24.B32@0:8@"UI (从右到左每4个字节0x34322279) #mark-1.1 0x104c42828 <+44>: adrp x8, 0 0x104c4282c <+48>: add x8, x8, #0x8c0 ; =0x8c0 #[x29, #-0x10]就是#mark-1位置存的x8参数值=1 0x104c42830 <+52>: ldur x9, [x29, #-0x10] 0x104c42834 <+56>: ldrsw x10, [x8, x9, lsl #2] #mark-2 #x8=x10地址+18地址 #mark-3 0x104c42838 <+60>: add x8, x10, x8 0x104c4283c <+64>: br x8 0x104c42840 <+68>: adrp x0, 1 0x104c42844 <+72>: add x0, x0, #0xf10 ; =0xf10 0x104c42848 <+76>: bl 0x104c42be0 ; symbol stub for: printf 0x104c4284c <+80>: str w0, [sp, #0x18] 0x104c42850 <+84>: b 0x104c428b4 ; <+184> at main.m:35 0x104c42854 <+88>: adrp x0, 1 0x104c42858 <+92>: add x0, x0, #0xf15 ; =0xf15 0x104c4285c <+96>: bl 0x104c42be0 ; symbol stub for: printf 0x104c42860 <+100>: str w0, [sp, #0x14] 0x104c42864 <+104>: b 0x104c428b4 ; <+184> at main.m:35 0x104c42868 <+108>: adrp x0, 1 0x104c4286c <+112>: add x0, x0, #0xf1a ; =0xf1a 0x104c42870 <+116>: bl 0x104c42be0 ; symbol stub for: printf 0x104c42874 <+120>: str w0, [sp, #0x10] 0x104c42878 <+124>: b 0x104c428b4 ; <+184> at main.m:35 0x104c4287c <+128>: adrp x0, 1 0x104c42880 <+132>: add x0, x0, #0xf1f ; =0xf1f 0x104c42884 <+136>: bl 0x104c42be0 ; symbol stub for: printf 0x104c42888 <+140>: str w0, [sp, #0xc] 0x104c4288c <+144>: b 0x104c428b4 ; <+184> at main.m:35 0x104c42890 <+148>: adrp x0, 1 0x104c42894 <+152>: add x0, x0, #0xf24 ; =0xf24 0x104c42898 <+156>: bl 0x104c42be0 ; symbol stub for: printf 0x104c4289c <+160>: str w0, [sp, #0x8] 0x104c428a0 <+164>: b 0x104c428b4 ; <+184> at main.m:35 0x104c428a4 <+168>: adrp x0, 1 0x104c428a8 <+172>: add x0, x0, #0xf29 ; =0xf29 0x104c428ac <+176>: bl 0x104c42be0 ; symbol stub for: printf 0x104c428b0 <+180>: str w0, [sp, #0x4] 0x104c428b4 <+184>: ldp x29, x30, [sp, #0x30] 0x104c428b8 <+188>: add sp, sp, #0x40 ; =0x40 0x104c428bc <+192>: ret |
问题:
#mark-1.1 取值多少?
根据adrp运行得知x8=0x104c438c0,然后通过lldb取出内存分布
lldb: memory read 0x104c438c0
0x104c438c0: 79 22 32 34 00 42 33 32 40 30 3a 38 40 22 55 49 ……..
(从右到左没4个字节(2bit位=1字节)如0x34322279)
#mark-2 ldrsw x10, [x8, x9, lsl #2] 什么意思?
x8+ (以x8作为基地址,x9左移2位),由上面分析可知x9=1,
- x9(1)左移2位是 4
- x8+4也就是上面的79 22 32 34 00 42 33 32 40 30 3a 38 40 22 55 49….向右偏移4个字节,也就是从00…开始
- x10=00 42 33 32,也就是0x32334200
- register read x10 可以验证第3步地址对不对
register read x10 读取 x10 = 0xffffffffffffffa8 也就是个负数 (0xff-0xa8+1) =0x58 (十进制-88) (全是ffffff是-1)
#mark-3 0x104c42838 <+60>: add x8, x10, x8 ?
通过上面2步知道:
x8= x10是个负数, x8地址-x10地址
如果case 202: ?
解:w0直接减200来判断 (一样来查表)
如果case是乱序的呢?switch分支有空缺 比如 case 1: case 5: case 9: case 11:
它一样是制作一张表,拿空间换时间 把中间跳过的数补充default地址 可看#mark-1.1来查看x8地址 如下图
如果是乱序呢?
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 |
void func(int a) { switch (a) { case 1: printf("1111"); break; case 59: printf("2222"); break; case 99: printf("3333"); break; case 110: printf("4444"); break; default: printf("default"); break; } } int main(int argc, char * argv[]) { func(3); } |
解析:
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 45 46 47 48 49 50 51 52 53 54 |
huibian-switch`func: 0x102f0a804 <+0>: sub sp, sp, #0x40 ; =0x40 0x102f0a808 <+4>: stp x29, x30, [sp, #0x30] 0x102f0a80c <+8>: add x29, sp, #0x30 ; =0x30 0x102f0a810 <+12>: stur w0, [x29, #-0x4] -> 0x102f0a814 <+16>: ldur w0, [x29, #-0x4] 0x102f0a818 <+20>: mov x8, x0 0x102f0a81c <+24>: subs w0, w0, #0x1 ; =0x1 0x102f0a820 <+28>: stur w8, [x29, #-0x8] 0x102f0a824 <+32>: stur w0, [x29, #-0xc] 0x102f0a828 <+36>: b.eq 0x102f0a86c ; <+104> at main.m:17 0x102f0a82c <+40>: b 0x102f0a830 ; <+44> at main.m 0x102f0a830 <+44>: ldur w8, [x29, #-0x8] 0x102f0a834 <+48>: subs w9, w8, #0x3b ; =0x3b 0x102f0a838 <+52>: stur w9, [x29, #-0x10] 0x102f0a83c <+56>: b.eq 0x102f0a880 ; <+124> at main.m:20 0x102f0a840 <+60>: b 0x102f0a844 ; <+64> at main.m 0x102f0a844 <+64>: ldur w8, [x29, #-0x8] 0x102f0a848 <+68>: subs w9, w8, #0x63 ; =0x63 0x102f0a84c <+72>: stur w9, [x29, #-0x14] 0x102f0a850 <+76>: b.eq 0x102f0a894 ; <+144> at main.m:23 0x102f0a854 <+80>: b 0x102f0a858 ; <+84> at main.m 0x102f0a858 <+84>: ldur w8, [x29, #-0x8] 0x102f0a85c <+88>: subs w9, w8, #0x6e ; =0x6e 0x102f0a860 <+92>: str w9, [sp, #0x18] 0x102f0a864 <+96>: b.eq 0x102f0a8a8 ; <+164> at main.m:26 0x102f0a868 <+100>: b 0x102f0a8bc ; <+184> at main.m:29 0x102f0a86c <+104>: adrp x0, 1 0x102f0a870 <+108>: add x0, x0, #0xf14 ; =0xf14 0x102f0a874 <+112>: bl 0x102f0abe4 ; symbol stub for: printf 0x102f0a878 <+116>: str w0, [sp, #0x14] 0x102f0a87c <+120>: b 0x102f0a8cc ; <+200> at main.m:32 0x102f0a880 <+124>: adrp x0, 1 0x102f0a884 <+128>: add x0, x0, #0xf19 ; =0xf19 0x102f0a888 <+132>: bl 0x102f0abe4 ; symbol stub for: printf 0x102f0a88c <+136>: str w0, [sp, #0x10] 0x102f0a890 <+140>: b 0x102f0a8cc ; <+200> at main.m:32 0x102f0a894 <+144>: adrp x0, 1 0x102f0a898 <+148>: add x0, x0, #0xf1e ; =0xf1e 0x102f0a89c <+152>: bl 0x102f0abe4 ; symbol stub for: printf 0x102f0a8a0 <+156>: str w0, [sp, #0xc] 0x102f0a8a4 <+160>: b 0x102f0a8cc ; <+200> at main.m:32 0x102f0a8a8 <+164>: adrp x0, 1 0x102f0a8ac <+168>: add x0, x0, #0xf23 ; =0xf23 0x102f0a8b0 <+172>: bl 0x102f0abe4 ; symbol stub for: printf 0x102f0a8b4 <+176>: str w0, [sp, #0x8] 0x102f0a8b8 <+180>: b 0x102f0a8cc ; <+200> at main.m:32 0x102f0a8bc <+184>: adrp x0, 1 0x102f0a8c0 <+188>: add x0, x0, #0xf28 ; =0xf28 0x102f0a8c4 <+192>: bl 0x102f0abe4 ; symbol stub for: printf 0x102f0a8c8 <+196>: str w0, [sp, #0x4] 0x102f0a8cc <+200>: ldp x29, x30, [sp, #0x30] 0x102f0a8d0 <+204>: add sp, sp, #0x40 ; =0x40 0x102f0a8d4 <+208>: ret |
这个完全就失去switch的优势,就是if else
Switch
1、假设switch语句的分支比较少的时候 小于4个相当于if
2、各个分支常量的差值较大的时候,编译器会在效率还是内存进行取舍(这个时候编译器还是会编译成类似于if,else的结构)
3、在分支比较多的时候:在编译的时候会生成一个表(跳转表每个地址四个字节)通过地址直接跳转 (最好是连续的case)