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。