1. 指针
指针也就是内存地址,指针变量是用来存放内存地址的变量。不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。
可使用 & 运算符访问地址。
之前的文章中有过说明,指针在内存中占8个字节。
可以是用sizeof来打印指针的size。
void func() {
int *a;
a = (int *)100;
a ++;
printf("%d", a);
}
这里定义一个int类型的指针a,然后赋值位100,我们知道指针的size是8个字节,a++之后打印多少?
答案是104。是的,没有看错,这里是因为指针的自增和自减操作,与执行的数据类型的宽度有关。
如果a = (char *)100
,则打印的就是101。
void func() {
int *a;
a = (int *)100;
a = a + 1;
printf("%d", a);
}
这个不是指针的自增、自减了,这个时候就跟指针的size有关了,打印108。
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 二级指针
void func() {
int **a;
a = (int **)100;
a = a + 1;
printf("%d", a);
}
这个时候a运算时,执行的类型就是 char *
类型,这是一个指针,8个字节。所以结果就是108。
2. 指针的汇编
void func() {
int *a;
int b = 10;
a = &b;
}
按照我们正常的理解,上述代码的意思就是把b的地址给到a,这个时候*a=10
。
看一下上面的代码汇编之后是什么样子的。
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打印一下相关数据:
(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 数组
void func() {
int arr[5] = {1,2,3,4,5};
for (int i = 0; i < 5; i++) {
printf("%d\n", *(arr + i));
}
}
2.2 野指针
void func() {
char *p;
char a = *p;
}
通过代码,我们知道,只是把*p的值给了a
为什么会发生野指针呢?
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寄存器的值的地址
(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。