一. switch(上)
我们继续汇编(四)switch内容开始
// main.m文件代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void funcA(int a){
switch (a) {
case 1:
printf("打坐");
break;
case 2:
printf("加红");
break;
case 3:
printf("加蓝");
break;
default:
printf("啥都不干");
break;
}
}
int main(int argc, char * argv[]) {
funcA(1);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
004--选择`funcA:
0x1009b2114 <+0>: sub sp, sp, #0x20 ; =0x20
0x1009b2118 <+4>: stp x29, x30, [sp, #0x10]
0x1009b211c <+8>: add x29, sp, #0x10 ; =0x10
0x1009b2120 <+12>: stur w0, [x29, #-0x4]
-> 0x1009b2124 <+16>: ldur w8, [x29, #-0x4]
// w8寄存器与 1比较,相等跳转0x1009b2158,否则跳转0x1009b2138
0x1009b2128 <+20>: cmp w8, #0x1 ; =0x1
0x1009b212c <+24>: str w8, [sp, #0x8]
0x1009b2130 <+28>: b.eq 0x1009b2158 ; <+68> at main.m
// 这里的跳转0x1009b2138 不写也会执行下面代码0x1009b2138
0x1009b2134 <+32>: b 0x1009b2138 ; <+36> at main.m
0x1009b2138 <+36>: ldr w8, [sp, #0x8]
0x1009b213c <+40>: cmp w8, #0x23 ; =0x23
0x1009b2140 <+44>: b.eq 0x1009b2178 ; <+100> at main.m
0x1009b2144 <+48>: b 0x1009b2148 ; <+52> at main.m
0x1009b2148 <+52>: ldr w8, [sp, #0x8]
0x1009b214c <+56>: cmp w8, #0xc8 ; =0xc8
0x1009b2150 <+60>: b.eq 0x1009b2168 ; <+84> at main.m
0x1009b2154 <+64>: b 0x1009b2188 ; <+116> at main.m
// 取出常量,执行printf打印
0x1009b2158 <+68>: adrp x0, 1
0x1009b215c <+72>: add x0, x0, #0xf6d ; =0xf6d
0x1009b2160 <+76>: bl 0x1009b258c ; symbol stub for: printf
0x1009b2164 <+80>: b 0x1009b2194 ; <+128> at main.m:27:1
// 取出常量,执行printf打印
0x1009b2168 <+84>: adrp x0, 1
0x1009b216c <+88>: add x0, x0, #0xf74 ; =0xf74
0x1009b2170 <+92>: bl 0x1009b258c ; symbol stub for: printf
0x1009b2174 <+96>: b 0x1009b2194 ; <+128> at main.m:27:1
// 取出常量,执行printf打印
0x1009b2178 <+100>: adrp x0, 1
0x1009b217c <+104>: add x0, x0, #0xf7b ; =0xf7b
0x1009b2180 <+108>: bl 0x1009b258c ; symbol stub for: printf
0x1009b2184 <+112>: b 0x1009b2194 ; <+128> at main.m:27:1
0x1009b2188 <+116>: adrp x0, 1
0x1009b218c <+120>: add x0, x0, #0xf82 ; =0xf82
0x1009b2190 <+124>: bl 0x1009b258c ; symbol stub for: printf
0x1009b2194 <+128>: ldp x29, x30, [sp, #0x10]
0x1009b2198 <+132>: add sp, sp, #0x20 ; =0x20
0x1009b219c <+136>: ret
// 如果是三个case,底层实际上执行的是if else
修改main.m代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void funcA(int a){
switch (a) {//
case 1:
printf("打坐");
break;
case 2:
printf("加红");
break;
case 3:
printf("加蓝");
break;
case 5:
printf("打怪");
break;
default:
printf("啥都不干");
break;
}
}
int main(int argc, char * argv[]) {
funcA(4);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
004--选择`funcA:
0x10427e0ec <+0>: sub sp, sp, #0x20 ; =0x20
0x10427e0f0 <+4>: stp x29, x30, [sp, #0x10]
0x10427e0f4 <+8>: add x29, sp, #0x10 ; =0x10
// 参数入栈,用w8来进行比较
0x10427e0f8 <+12>: stur w0, [x29, #-0x4]
-> 0x10427e0fc <+16>: ldur w8, [x29, #-0x4]
0x10427e100 <+20>: subs w8, w8, #0x1 ; =0x1
0x10427e104 <+24>: mov x9, x8
0x10427e108 <+28>: ubfx x9, x9, #0, #32
0x10427e10c <+32>: cmp x9, #0x4 ; =0x4
0x10427e110 <+36>: str x9, [sp]
0x10427e114 <+40>: b.hi 0x10427e170 ; <+132> at main.m
0x10427e118 <+44>: adrp x8, 0
0x10427e11c <+48>: add x8, x8, #0x188 ; =0x188
0x10427e120 <+52>: ldr x11, [sp]
0x10427e124 <+56>: ldrsw x10, [x8, x11, lsl #2]
0x10427e128 <+60>: add x9, x8, x10
0x10427e12c <+64>: br x9
0x10427e130 <+68>: adrp x0, 1
0x10427e134 <+72>: add x0, x0, #0xf69 ; =0xf69
0x10427e138 <+76>: bl 0x10427e588 ; symbol stub for: printf
0x10427e13c <+80>: b 0x10427e17c ; <+144> at main.m:29:1
0x10427e140 <+84>: adrp x0, 1
0x10427e144 <+88>: add x0, x0, #0xf70 ; =0xf70
0x10427e148 <+92>: bl 0x10427e588 ; symbol stub for: printf
0x10427e14c <+96>: b 0x10427e17c ; <+144> at main.m:29:1
0x10427e150 <+100>: adrp x0, 1
0x10427e154 <+104>: add x0, x0, #0xf77 ; =0xf77
0x10427e158 <+108>: bl 0x10427e588 ; symbol stub for: printf
0x10427e15c <+112>: b 0x10427e17c ; <+144> at main.m:29:1
0x10427e160 <+116>: adrp x0, 1
0x10427e164 <+120>: add x0, x0, #0xf7e ; =0xf7e
0x10427e168 <+124>: bl 0x10427e588 ; symbol stub for: printf
0x10427e16c <+128>: b 0x10427e17c ; <+144> at main.m:29:1
0x10427e170 <+132>: adrp x0, 1
0x10427e174 <+136>: add x0, x0, #0xf85 ; =0xf85
0x10427e178 <+140>: bl 0x10427e588 ; symbol stub for: printf
0x10427e17c <+144>: ldp x29, x30, [sp, #0x10]
0x10427e180 <+148>: add sp, sp, #0x20 ; =0x20
0x10427e184 <+152>: ret

0x10427e100 <+20>: subs w8, w8, #0x1 ; =0x1 subs 减的话有可能溢出,下面进行验证
void funcA(int a){
switch (a) {//
case 5:
printf("打坐");
break;
case 6:
printf("加红");
break;
case 7:
printf("加蓝");
break;
case 8:
printf("打怪");
break;
default:
printf("啥都不干");
break;
}
}
int main(int argc, char * argv[]) {
funcA(4);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 汇编代码
004--选择`funcA:
0x10040e0f0 <+0>: sub sp, sp, #0x20 ; =0x20
0x10040e0f4 <+4>: stp x29, x30, [sp, #0x10]
0x10040e0f8 <+8>: add x29, sp, #0x10 ; =0x10
0x10040e0fc <+12>: stur w0, [x29, #-0x4]
// 给w8 赋值为4,把之前的值清干净
-> 0x10040e100 <+16>: ldur w8, [x29, #-0x4]
// 这里减的是5,就会溢出
0x10040e104 <+20>: subs w8, w8, #0x5 ; =0x5
// 执行完上面一行,x8 unsigned long 0x00000000ffffffff
// x8给到x9,x9也变成0x00000000ffffffff
0x10040e108 <+24>: mov x9, x8
// ubfx指令是指,把x9寄存器由低往高数32位,其他位清0,这里保留的是 ffffffff,相当于把高32位清0
// 因为这里只取了低32位的值,所以要把高32位清0
0x10040e10c <+28>: ubfx x9, x9, #0, #32
// 把高32位清掉再来比较,防止比较出现问题
0x10040e110 <+32>: cmp x9, #0x3 ; =0x3
0x10040e114 <+36>: str x9, [sp]
0x10040e118 <+40>: b.hi 0x10040e174 ; <+132> at main.m
...
0x10040e10c <+28>: ubfx x9, x9, #0, #32,下面验证ubfx是把x9寄存器由低往高数32位,其他位清0


上面两张图可以看出ubfx是把x9寄存器由低往高数32位,其他位清0
继续修改代码如下
void funcA(int a){
switch (a) {
case 4:
printf("打坐");
break;
case 6:
printf("加红");
break;
case 7:
printf("加蓝");
break;
case 8:
printf("打怪");
break;
default:
printf("啥都不干");
break;
}
}
int main(int argc, char * argv[]) {
funcA(5);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 汇编代码如下
004--选择`funcA:
0x1009860ec <+0>: sub sp, sp, #0x20 ; =0x20
0x1009860f0 <+4>: stp x29, x30, [sp, #0x10]
0x1009860f4 <+8>: add x29, sp, #0x10 ; =0x10
0x1009860f8 <+12>: stur w0, [x29, #-0x4]
0x1009860fc <+16>: ldur w8, [x29, #-0x4]
0x100986100 <+20>: subs w8, w8, #0x4 ; =0x4
// 传进来参数是5,减去4之后 x8 x9 都为1
0x100986104 <+24>: mov x9, x8
// x9寄存器高32位清0,这里x9值还是1
-> 0x100986108 <+28>: ubfx x9, x9, #0, #32
// 这里是 1跟4比较,小于继续执行0x100986118
0x10098610c <+32>: cmp x9, #0x4 ; =0x4
0x100986110 <+36>: str x9, [sp]
// 无符号大于就执行0x100986170,0x100986170跳的就是default条件
0x100986114 <+40>: b.hi 0x100986170 ; <+132> at main.m
// 1<4 执行这一块汇编代码,这里的x8都是一些连续的负数,可以看下图
0x100986118 <+44>: adrp x8, 0
0x10098611c <+48>: add x8, x8, #0x188 ; =0x188
// 通过上面可以看出x11的值为1
0x100986120 <+52>: ldr x11, [sp]
// x11寄存器的值先左移两位得到4,4在加上x8寄存器的值 最后赋值给x10寄存器,x10的值为-84看下图
0x100986124 <+56>: ldrsw x10, [x8, x11, lsl #2]
// x8加上x10的值赋值给x9寄存器
0x100986128 <+60>: add x9, x8, x10
// br b是跳转指令,r就是register,br表示根据寄存器里面的值作为地址值进行跳转,这里表示跳转到x9寄存器中存的地址值
// 通过计算得到x9的地址为0x0000000100986170,也就是执行default的逻辑
0x10098612c <+64>: br x9
// 这里是一个case
0x100986130 <+68>: adrp x0, 1
0x100986134 <+72>: add x0, x0, #0xf69 ; =0xf69
0x100986138 <+76>: bl 0x100986588 ; symbol stub for: printf
0x10098613c <+80>: b 0x10098617c ; <+144> at main.m:29:1
// 一个case
0x100986140 <+84>: adrp x0, 1
0x100986144 <+88>: add x0, x0, #0xf70 ; =0xf70
0x100986148 <+92>: bl 0x100986588 ; symbol stub for: printf
0x10098614c <+96>: b 0x10098617c ; <+144> at main.m:29:1
// 一个case
0x100986150 <+100>: adrp x0, 1
0x100986154 <+104>: add x0, x0, #0xf77 ; =0xf77
0x100986158 <+108>: bl 0x100986588 ; symbol stub for: printf
0x10098615c <+112>: b 0x10098617c ; <+144> at main.m:29:1
// 一个case
0x100986160 <+116>: adrp x0, 1
0x100986164 <+120>: add x0, x0, #0xf7e ; =0xf7e
0x100986168 <+124>: bl 0x100986588 ; symbol stub for: printf
0x10098616c <+128>: b 0x10098617c ; <+144> at main.m:29:1
0x100986170 <+132>: adrp x0, 1
0x100986174 <+136>: add x0, x0, #0xf85 ; =0xf85
0x100986178 <+140>: bl 0x100986588 ; symbol stub for: printf
0x10098617c <+144>: ldp x29, x30, [sp, #0x10]
0x100986180 <+148>: add sp, sp, #0x20 ; =0x20
0x100986184 <+152>: ret



二. switch(下)
// 代码修改如下,查看汇编代码
void funcA(int a){
switch (a) {
case 5:
printf("打坐");
break;
case 6:
printf("加红");
break;
case 7:
printf("加蓝");
break;
case 8:
printf("打怪");
break;
default:
printf("啥都不干");
break;
}
}
int main(int argc, char * argv[]) {
funcA(5);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
004--选择`funcA:
0x10026a0f0 <+0>: sub sp, sp, #0x20 ; =0x20
0x10026a0f4 <+4>: stp x29, x30, [sp, #0x10]
0x10026a0f8 <+8>: add x29, sp, #0x10 ; =0x10
0x10026a0fc <+12>: stur w0, [x29, #-0x4]
-> 0x10026a100 <+16>: ldur w8, [x29, #-0x4]
// 通过修改不同case值查看汇编,发现这里减的是最小的case值
// 编译器在编译funcA的时候必然会知道最小的case值是多少,却不一定知道参数a的值,因为参数a的值有可能是从网络获取
0x10026a104 <+20>: subs w8, w8, #0x5 ; =0x5
0x10485e108 <+24>: mov x9, x8
// 高32位清0
0x10485e10c <+28>: ubfx x9, x9, #0, #32
// x9 与 #0x3进行比较,3是最大case值与最小case值的差值(通过修改不同case值,查看汇编发现)
// 下面三行的算法: x9为传入的参数减去最小case得到,3为最大case值与最小case的差值,如果x9无符号大于3直接跳转default执行
0x10485e110 <+32>: cmp x9, #0x3 ; =0x3
0x10485e114 <+36>: str x9, [sp]
// 这里请思考一个问题,为什么是无符号大于?不能是有符号吗?
// 如果x9的值为负数的话,就会小于3,依然会去比较case。如果是如符号大于的话,就会避免负数的情况,因为无符号的话,计算机会把负数转化成一个非常大的数
0x10485e118 <+40>: b.hi 0x10485e174 ; <+132> at main.m
// 如果执行到这里,就说明参数a一定在 最大case 与 最小case之间
// 下面两行得到地址为0x10485e18c,这个地址就是ret地址0x10485e188后面的4个字节,代码的后面就有0x10485e18c这样一串负数,这串负数就是一张表格
0x10485e11c <+44>: adrp x8, 0
0x10485e120 <+48>: add x8, x8, #0x18c
// 直接通过一轮运算,拿出参数减去最小case值之后的值
0x10485e124 <+52>: ldr x11, [sp]
// 左移2位之后乘以x11的差值,再加上x8的地址,这里实际上计算的是偏移量值赋给x10,这里为什么是左移2位?因为表里是以4个字节为单位的
0x10485e128 <+56>: ldrsw x10, [x8, x11, lsl #2]
// 通过偏移量找到不同case对应的地址进行跳转执行
0x10485e12c <+60>: add x9, x8, x10
0x10485e130 <+64>: br x9
0x10485e134 <+68>: adrp x0, 1
0x10485e138 <+72>: add x0, x0, #0xf69 ; =0xf69
0x10485e13c <+76>: bl 0x10485e588 ; symbol stub for: printf
0x10485e140 <+80>: b 0x10485e180 ; <+144> at main.m:29:1
0x10485e144 <+84>: adrp x0, 1
0x10485e148 <+88>: add x0, x0, #0xf70 ; =0xf70
0x10485e14c <+92>: bl 0x10485e588 ; symbol stub for: printf
0x10485e150 <+96>: b 0x10485e180 ; <+144> at main.m:29:1
0x10485e154 <+100>: adrp x0, 1
0x10485e158 <+104>: add x0, x0, #0xf77 ; =0xf77
0x10485e15c <+108>: bl 0x10485e588 ; symbol stub for: printf
0x10485e160 <+112>: b 0x10485e180 ; <+144> at main.m:29:1
0x10485e164 <+116>: adrp x0, 1
0x10485e168 <+120>: add x0, x0, #0xf7e ; =0xf7e
0x10485e16c <+124>: bl 0x10485e588 ; symbol stub for: printf
0x10485e170 <+128>: b 0x10485e180 ; <+144> at main.m:29:1
0x10485e174 <+132>: adrp x0, 1
0x10485e178 <+136>: add x0, x0, #0xf85 ; =0xf85
0x10485e17c <+140>: bl 0x10485e588 ; symbol stub for: printf
0x10485e180 <+144>: ldp x29, x30, [sp, #0x10]
0x10485e184 <+148>: add sp, sp, #0x20 ; =0x20
0x10485e188 <+152>: ret
这里能通过表头地址0x10485e18c减去不同偏移量计算,是因为switch代码分支是连续的,创建的这张表里放入偏移量就可以了,最终通过偏移量找到对应的跳转地址(通过内存空间换取时间)

小结
- 先将参数减去最小case值得到差值
- 再获取到最小case值与最大case值的差值范围
- 如果参数与最小case的差值,超出最小case值与最大case差值范围,就执行default,否则继续执行
- 参数与最小case值的差值,映射到表中偏移量。通过表中偏移量计算出地址去执行。注意⚠️这个表里存的是偏移差值(原因有两点:1 地址太长 2 地址在运行的时候才会知道虚拟地址,如果这里存偏移地址的话仍然要加aslr,不如直接算偏移值)
Switch
1、假设switch语句的分支比较少的时候(例如3,少于4的时候没有意义)没有必要使用此结构,相当于if。
2、各个分支常量的差值较大的时候,编译器会在效率还是内存进行取舍,这个时候编译器还是会编译成类似于if,else的结构。
3、在分支比较多的时候:在编译的时候会生成一个表(跳转表每个地址四个字节)。
三. 编译器优化
创建空工程 002--编译器优化,编译器优化等级可以在这里配置,debug环境下默认不优化,release环境下默认最快最小模式。其中最后面两种优化方式 Fastest, Aggressive... 是比较偏激的优化方式,用内存空间换时间

// main.m代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
int a = 1;
int b = 2;
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 编译器优化模式为None,汇编代码如下
002--编译器优化`main:
0x1041be1d4 <+0>: sub sp, sp, #0x40 ; =0x40
0x1041be1d8 <+4>: stp x29, x30, [sp, #0x30]
0x1041be1dc <+8>: add x29, sp, #0x30 ; =0x30
0x1041be1e0 <+12>: stur wzr, [x29, #-0x4]
0x1041be1e4 <+16>: stur w0, [x29, #-0x8]
0x1041be1e8 <+20>: stur x1, [x29, #-0x10]
0x1041be1ec <+24>: mov w8, #0x1 // 参数a=1
0x1041be1f0 <+28>: stur w8, [x29, #-0x14]
0x1041be1f4 <+32>: mov w8, #0x2 // 参数b=2
0x1041be1f8 <+36>: str w8, [sp, #0x18]
-> 0x1041be1fc <+40>: ldur w0, [x29, #-0x8]
0x1041be200 <+44>: ldur x1, [x29, #-0x10]
0x1041be204 <+48>: adrp x9, 7
0x1041be208 <+52>: add x9, x9, #0x398 ; =0x398
0x1041be20c <+56>: ldr x9, [x9]
0x1041be210 <+60>: str w0, [sp, #0x14]
0x1041be214 <+64>: mov x0, x9
...
// 编译器优化模式为Fastest, Smallest,汇编代码如下,发现没有看到参数1 和 参数2 ,没用到的局部变量或者全局变量,编译器会删掉
002--编译器优化`main:
0x10434a470 <+0>: stp x22, x21, [sp, #-0x30]!
0x10434a474 <+4>: stp x20, x19, [sp, #0x10]
0x10434a478 <+8>: stp x29, x30, [sp, #0x20]
0x10434a47c <+12>: add x29, sp, #0x20 ; =0x20
0x10434a480 <+16>: mov x19, x1
0x10434a484 <+20>: mov x20, x0
-> 0x10434a488 <+24>: nop
0x10434a48c <+28>: ldr x0, #0x6f14 ; (void *)0x0000000104351430: AppDelegate
0x10434a490 <+32>: bl 0x10434a570 ; symbol stub for: objc_opt_class
0x10434a494 <+36>: bl 0x10434a528 ; symbol stub for: NSStringFromClass
0x10434a498 <+40>: mov x29, x29
0x10434a49c <+44>: bl 0x10434a594 ; symbol stub for: objc_retainAutoreleasedReturnValue
0x10434a4a0 <+48>: mov x21, x0
0x10434a4a4 <+52>: mov x0, x20
0x10434a4a8 <+56>: mov x1, x19
...
// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int sum(int a,int b){
return a + b;
}
int main(int argc, char * argv[]) {
int a = sum(1,2);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 编译器优化模式为Fastest, Smallest,汇编代码如下,没有发现调用sum函数,因为int a = sum(1,2);代码对程序的执行结果没有影响,编译器会优化删掉
002--编译器优化`main:
0x1045f2470 <+0>: stp x22, x21, [sp, #-0x30]!
0x1045f2474 <+4>: stp x20, x19, [sp, #0x10]
0x1045f2478 <+8>: stp x29, x30, [sp, #0x20]
0x1045f247c <+12>: add x29, sp, #0x20 ; =0x20
0x1045f2480 <+16>: mov x19, x1
0x1045f2484 <+20>: mov x20, x0
-> 0x1045f2488 <+24>: nop
0x1045f248c <+28>: ldr x0, #0x6f14 ; (void *)0x00000001045f9430: AppDelegate
0x1045f2490 <+32>: bl 0x1045f2570 ; symbol stub for: objc_opt_class
0x1045f2494 <+36>: bl 0x1045f2528 ; symbol stub for: NSStringFromClass
0x1045f2498 <+40>: mov x29, x29
0x1045f249c <+44>: bl 0x1045f2594 ; symbol stub for: objc_retainAutoreleasedReturnValue
...
// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int sum(int a,int b){
return a + b;
}
int main(int argc, char * argv[]) {
int a = sum(1,2);
NSLog(@"%d",a);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 编译器优化模式为Fastest, Smallest,汇编代码如下
002--编译器优化`main:
0x102bea438 <+0>: sub sp, sp, #0x40 ; =0x40
0x102bea43c <+4>: stp x22, x21, [sp, #0x10]
0x102bea440 <+8>: stp x20, x19, [sp, #0x20]
0x102bea444 <+12>: stp x29, x30, [sp, #0x30]
0x102bea448 <+16>: add x29, sp, #0x30 ; =0x30
0x102bea44c <+20>: mov x19, x1
0x102bea450 <+24>: mov x20, x0
// 直接计算出结果3,把sun函数优化掉
0x102bea454 <+28>: mov w8, #0x3
// w8寄存器入栈
0x102bea458 <+32>: str x8, [sp]
0x102bea45c <+36>: adr x0, #0x1bcc ; @"%d"
0x102bea460 <+40>: nop
// NSLog系统函数,参数通过栈传递
0x102bea464 <+44>: bl 0x102bea50c ; symbol stub for: NSLog
-> 0x102bea468 <+48>: nop
...
四. 指针的基础常识
创建空工程 003--指针
// main.m代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func(){
int *a; //指针的宽度是8字节
printf("%lu",sizeof(a));
}
int main(int argc, char * argv[]) {
func();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 打印结果是8(lldb)
// 汇编代码如下
003--指针`func:
0x100f72194 <+0>: sub sp, sp, #0x20 ; =0x20
0x100f72198 <+4>: stp x29, x30, [sp, #0x10]
0x100f7219c <+8>: add x29, sp, #0x10 ; =0x10
0x100f721a0 <+12>: adrp x0, 1
0x100f721a4 <+16>: add x0, x0, #0xf8d ; =0xf8d
// sp的值给到x8,x8就相当于栈
-> 0x100f721a8 <+20>: mov x8, sp
// 指针的宽度是8
0x100f721ac <+24>: mov x9, #0x8
// x9放入栈
0x100f721b0 <+28>: str x9, [x8]
0x100f721b4 <+32>: bl 0x100f725ac ; symbol stub for: printf
0x100f721b8 <+36>: ldp x29, x30, [sp, #0x10]
0x100f721bc <+40>: add sp, sp, #0x20 ; =0x20
0x100f721c0 <+44>: ret
// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func(){
int *a;
a = (int *)100;
a++; //指针的自增自减和执行的数据类型宽度有关
printf("%d",a);
}
int main(int argc, char * argv[]) {
func();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 打印结果是104
// 如果func函数修改如下,打印结果就是101
char *a;
a = (char *)100;
a++;
printf("%d",a);
// func函数修改如下,打印结果就是108
int **a;
a = (int **)100;
a++;
printf("%d",a);
// func函数修改如下,打印结果就是108
int **a;
a = (int **)100;
a = a + 1;
printf("%d",a);
小结
- 指针的自增自减与编译器有关,所以指针最好使用 a = a + 1,别使用a++
- 指针的加减都是和指向的数据类型宽度有关
// main.m代码修改如下,来查看指针的减法运算
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func(){
int *a;
a = (int *)100;
int *b;
b = (int *)200;
int x = a - b;
printf("%d",x);
}
int main(int argc, char * argv[]) {
func();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 100 - 200为 -100,-100再除以整型的宽度结果为 -25
// 打印结果是-25
// 指针也可以用来比大小
int *a;
a = (int *)100;
int *b;
b = (int *)200;
if (a > b) {
printf("a > b");
} else {
printf("a <= b");
}
小结
- 指针的运算单位是执行的数据类型宽度
- 数据类型是可以强制转换的,结构体的基本类型是不能强制转换的
- 任何的变量都可以使用取地址符号作为地址,来寻址取值
五. 指针的反汇编形式
// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func(){
int* a;
int b = 10;
a = &b;
printf("hello");
}
int main(int argc, char * argv[]) {
func();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 查看汇编代码如下
003--指针`func:
0x10076618c <+0>: sub sp, sp, #0x20 ; =0x20
0x100766190 <+4>: stp x29, x30, [sp, #0x10]
0x100766194 <+8>: add x29, sp, #0x10 ; =0x10
0x100766198 <+12>: add x8, sp, #0x4 ; =0x4
// w9的值为0xa,也就是10
0x10076619c <+16>: mov w9, #0xa
// w9入栈,相当于10变成局部变量
0x1007661a0 <+20>: str w9, [sp, #0x4]
// x8指向a的地址,说明栈区现在有一个变量,指向局部变量的地址,所以x8是一个指针变量,从x8~x10这一段是一个地址,保存的是指针
-> 0x1007661a4 <+24>: str x8, [sp, #0x8]
0x1007661a8 <+28>: adrp x0, 1
0x1007661ac <+32>: add x0, x0, #0xf89 ; =0xf89
0x1007661b0 <+36>: bl 0x1007665a8 ; symbol stub for: printf
0x1007661b4 <+40>: ldp x29, x30, [sp, #0x10]
0x1007661b8 <+44>: add sp, sp, #0x20 ; =0x20
0x1007661bc <+48>: ret
// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func(){
//注意⚠️ int *a == &arr[0] == arr 这几个是对等的关系!
int arr[5] = {1,2,3,4,5};
for (int i = 0; i < 5; i++) {
printf("%d",*(arr + i));
}
}
int main(int argc, char * argv[]) {
func();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 打印结果是12345
//func函数这样写,打印结果也是12345
int arr[5] = {1,2,3,4,5};
int * a = arr;
for (int i = 0; i < 5; i++) {
printf("%d",*(a++));
}
六. 指针的基本用法
// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void func(){
// p1是个指针,c是取p1里面的值
char * p1;
char c = *p1;
}
int main(int argc, char * argv[]) {
func();
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 汇编代码如下
003--指针`func:
0x1025b61c0 <+0>: sub sp, sp, #0x10 ; =0x10
// 栈里面作为一个地址取出一个值给到x8,这个值是0
0x1025b61c4 <+4>: ldr x8, [sp, #0x8]
// 用0地址再去取值,就报了错
-> 0x1025b61c8 <+8>: ldrb w9, [x8]
0x1025b61cc <+12>: strb w9, [sp, #0x7]
0x1025b61d0 <+16>: add sp, sp, #0x10 ; =0x10
0x1025b61d4 <+20>: ret
// 运行上面工程直接崩溃,野指针异常Thread 1: EXC_BAD_ACCESS (code=1, address=0x0),崩溃在用0地址去取值报错
// 上面func函数内容修改如下运行
char * p1;
char c = *p1;
char d = *(p1 + 0);
// 汇编代码如下
003--指针`func:
0x10494a1b4 <+0>: sub sp, sp, #0x10 ; =0x10
// p1指针指向[sp, #0x8]里面的值,[sp, #0x8]里面值全是0,相当于p1指向(X8)0x0
// char c = *p1;与 char d = *(p1 + 0); 都是从(X8)0x0里面取值
0x10494a1b8 <+4>: ldr x8, [sp, #0x8]
// 程序崩溃在这一行
-> 0x10494a1bc <+8>: ldrb w9, [x8]
0x10494a1c0 <+12>: strb w9, [sp, #0x7]
// (p1 + 0) 还用的是sp+8偏移地址
0x10494a1c4 <+16>: ldr x8, [sp, #0x8]
0x10494a1c8 <+20>: ldrb w9, [x8]
0x10494a1cc <+24>: strb w9, [sp, #0x6]
0x10494a1d0 <+28>: add sp, sp, #0x10 ; =0x10
0x10494a1d4 <+32>: ret
// 上面func函数内容修改如下运行
char * p1; //指针 --> (X8)0x0
char c = *p1; //从[x8]取值
char d = *(p1 + 1); // 从[x8,#0x1]取值,char类型的宽度是1
// 汇编代码如下
003--指针`func:
0x1047121b4 <+0>: sub sp, sp, #0x10 ; =0x10
0x1047121b8 <+4>: ldr x8, [sp, #0x8]
-> 0x1047121bc <+8>: ldrb w9, [x8]
0x1047121c0 <+12>: strb w9, [sp, #0x7]
0x1047121c4 <+16>: ldr x8, [sp, #0x8]
// 根据数据类型宽度添加地址取值
0x1047121c8 <+20>: ldrb w9, [x8, #0x1]
0x1047121cc <+24>: strb w9, [sp, #0x6]
0x1047121d0 <+28>: add sp, sp, #0x10 ; =0x10
0x1047121d4 <+32>: ret
// 代码修改如下地址就会加4,根据数据类型宽度相加
int * p1; //指针 --> (X8)0x0
int c = *p1; //[x8]
int d = *(p1 + 1); // [x8,#0x4]
// 代码修改如下地址就会加8
int ** p1; //指针 --> (X8)0x0
int *c = *p1; //[x8]
int *d = *(p1 + 1); // [x8,#0x8]
// 这里栈需要拉伸多少字节? 上面3个指针分别需要8字节,根据16字节对齐原则这里栈需要拉伸32字节,0x1027fe1b0 <+0>: sub sp, sp, #0x20
// 代码修改如下,4个指针32字节,栈刚好拉伸32字节
int ** p1; //指针 --> (X8)0x0
int *p2;
int *c = *p1; //[x8]
int *d = *(p1 + 1); // [x8,#0x1]
下面需要注意多级指针的取值
// 上面func函数内容修改如下运行
int ** p1;
char c = **p1;
// 汇编代码如下
003--指针`func:
0x104dae1bc <+0>: sub sp, sp, #0x10 ; =0x10
// 从栈里取出的初始值
0x104dae1c0 <+4>: ldr x8, [sp, #0x8]
// 用[x8]作为地址去取值,下面两次ldr [x8],说明是指针的指针也就是二级指针
-> 0x104dae1c4 <+8>: ldr x8, [x8]
0x104dae1c8 <+12>: ldr w9, [x8]
0x104dae1cc <+16>: strb w9, [sp, #0x7]
0x104dae1d0 <+20>: add sp, sp, #0x10 ; =0x10
0x104dae1d4 <+24>: ret
// 上面func函数内容修改如下运行
char ** p1; // char** 指向的是 char* 指针,数据宽度是8
char c = *(*(p1 + 2) + 2); //char* 指向的是char,数据宽度是1
char c2 = p1[1][2]; //这一行代码分别偏移#0x8字节 #0x2字节
// 汇编代码如下
003--指针`func:
0x1022b21bc <+0>: sub sp, sp, #0x10 ; =0x10
0x1022b21c0 <+4>: ldr x8, [sp, #0x8]
// 第一次加#0x10,因为指针指向的数据类型是char* ,所以是以8为宽度加2*8=16字节,偏移16字节
-> 0x1022b21c4 <+8>: ldr x8, [x8, #0x10]
// *(p1 + 2) 取出来之后数据类型是char,所以是以1为宽度加1*2=2字节,偏移8字节
0x1022b21c8 <+12>: ldrb w9, [x8, #0x2]
0x1022b21cc <+16>: strb w9, [sp, #0x7]
0x1022b21d0 <+20>: add sp, sp, #0x10 ; =0x10
0x1022b21d4 <+24>: ret
网友评论