1. 指针
指针也就是内存地址,指针变量是用来存放内存地址的变量。不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。
可使用 & 运算符访问地址。
之前的文章中有过说明,指针在内存中占8个字节。
可以是用sizeof来打印指针的size。
1 2 3 4 5 6 | void func() { int *a; a = (int *)100; a ++; printf("%d", a); } |
这里定义一个int类型的指针a,然后赋值位100,我们知道指针的size是8个字节,a++之后打印多少?
答案是104。是的,没有看错,这里是因为指针的自增和自减操作,与执行的数据类型的宽度有关。
如果a = (char *)100
,则打印的就是101。
1 2 3 4 5 6 | void func() { int *a; a = (int *)100; a = a + 1; printf("%d", a); } |
这个不是指针的自增、自减了,这个时候就跟指针的size有关了,打印108。
1 2 3 4 5 6 7 8 9 10 | void func_add() { int *a; a = (int *)100; int *b; b = (int *)200; int x = a - b; printf("x = %d", x); } |
先说答案,打印的结果是x = -25
。
a - b = -100, 然后除以4就得到了这个结果。
指针的运算单位是执行的数据类型的宽度。
1.1 二级指针
1 2 3 4 5 6 | void func() { int **a; a = (int **)100; a = a + 1; printf("%d", a); } |
这个时候a运算时,执行的类型就是 char *
类型,这是一个指针,8个字节。所以结果就是108。
2. 指针的汇编
1 2 3 4 5 | void func() { int *a; int b = 10; a = &b; } |
按照我们正常的理解,上述代码的意思就是把b的地址给到a,这个时候*a=10
。
看一下上面的代码汇编之后是什么样子的。
1 2 3 4 5 6 7 8 9 10 11 12 | Demo`func: 0x100206130 <+0>: sub sp, sp, #0x10 ; =0x10 // 1. x8 = sp + 0x4,x8指向这个位置 0x100206134 <+4>: add x8, sp, #0x4 ; =0x4 // 2. 局部变量,w9=10 0x100206138 <+8>: mov w9, #0xa // 3. 把w9的值放在x8所在的地址上。 0x10020613c <+12>: str w9, [sp, #0x4] // 4. 把x8存储的地址放在sp + 0x8的位置上。 -> 0x100206140 <+16>: str x8, [sp, #0x8] 0x100206144 <+20>: add sp, sp, #0x10 ; =0x10 0x100206148 <+24>: ret |
通过lldb打印一下相关数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | (lldb) register read sp sp = 0x000000016fbff880 (lldb) register read x8 x8 = 0x000000016fbff884 (lldb) register read x9 x9 = 0x000000000000000a // 打印一下x8寄存器里的内存地址情况,里头存的值是0xa (lldb) x 0x000000016fbff884 0x16fbff884: 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x16fbff894: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ // x8的地址放在了sp+0x8的位置,打印一下内存,就是x8存储的地址。 (lldb) x 0x000000016fbff888 0x16fbff888: 84 f8 bf 6f 01 00 00 00 00 00 00 00 00 00 00 00 ...o............ 0x16fbff898: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ |
2.1 数组
1 2 3 4 5 6 | void func() { int arr[5] = {1,2,3,4,5}; for (int i = 0; i < 5; i++) { printf("%d\n", *(arr + i)); } } |
2.2 野指针
1 2 3 4 | void func() { char *p; char a = *p; } |
通过代码,我们知道,只是把*p的值给了a
为什么会发生野指针呢?
1 2 3 4 5 6 7 8 9 | Demo`func: 0x100812134 <+0>: sub sp, sp, #0x10 ; =0x10 // 1. 因为p是指针。把sp + 0x8的地址中的值给x8 -> 0x100812138 <+4>: ldr x8, [sp, #0x8] // 2. 把x8寄存器中存的地址的值给w9 0x10081213c <+8>: ldrb w9, [x8] 0x100812140 <+12>: strb w9, [sp, #0x7] 0x100812144 <+16>: add sp, sp, #0x10 ; =0x10 0x100812148 <+20>: ret |
-
第一步寻址操作,获取x8寄存器的值的地址
1 2 3 4 5 6 7 8 9 10
(lldb) register read sp sp = 0x000000016f5f3880 // sp + 0x8 = 0x000000016f5f3888 (lldb) x 0x000000016f5f3888 0x16f5f3888: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0x16f5f3898: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ (lldb) register read x8 x8 = 0x0000000000000000
这里发现 x8寄存器中存的地址是空,全是0。
-
把x8寄存器中地址所在的值给w9。寻址操作
这里寻址是从0x00000000上找值,从空地址上找值,就会发生crash。