1. 学指针前先理解内存:
1.1 内存是硬件,是用于存放数据的硬件。程序执行前需要先放到内存中才能被CPU
处理。内存是与CPU
沟通的桥梁,计算机中所有程序的运行都要依靠内存,内存对计算机的影响非常大。我们知道计算上cpu(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那么这些内存空间该如何管理呢?其实也是把内存划分为一个个内存单元,每个内存单元的大小取1个字节。
1.2 我们举个生活中的例子,你是一名大一新生,你住在b栋学生公寓,但是公寓并没有房间编号,假设这时你的一个朋友要来找你玩,那么他如果想找到你,就得挨个挨个敲门找人,极其的麻烦与不方便,效率极低。但是我们这时只需一个操作:根据楼层和楼层房间的情况给房间编上号。那么无疑,朋友找你玩方便了太多。
我们将这个情景类比到计算机中,每个内存单元(一个字节),相当于公寓的一个8人间,每个人是一个比特位。每个内存单元也有一个编号(这个编号就相当于8人间的所属房间号),有了这个内存单元的编号,CPU就可以快速的找到一个内存空间。我们在生活中把房间号叫做地址,在计算机中我们把内存单元的编号称为地址。C语言中给地址起了个新名字:指针。
2.指针变量与地址
2.1取地址操作符(&)
理解了内存和地址的关系,我们再回到C语言,在C语言中创建变量其实就是在像内存创建空间,
vx环境下调试打开内存窗口输出&a如下图:
上述代码就是创建了整型变量a,内存中申请4个字节,用于存放整数10,其中每个字节都有地址。
那么我们该如何得到a的地址呢?
我们到这里就得学习一个取地址操作符&
虽然整型变量占用4个字节,我们只要知道了第一个字节地址,顺藤摸瓜访问到4个字节的数据也是可行的。
2.2指针变量和解引用操作符(*)
2.2.1指针变量
我们通过取地址操作符(&)拿到的地址是一个数值,那么这个数值如果想存储起来的话,应该存放在哪里呢?
这个答案是:指针变量。
指针变量也是一种变量,这种变量就是用来存放地址的,存放在指针变量中的值都会理解为地址。
2.2.2如何拆解指针类型
我们看到pa的类型是int*,我们该如何理解指针类型呢?
这里pa左边写的是int*,*是在说明pa是指针变量,而前面的int是在说明pa指向的是整型类型的对象。
2.2.3解引用操作符
我们将地址保存起来,未来是要使用的,那么要如何使用呢?
这时就需要学习解引用操作符(*)
我们只要拿到了地址(指针),就可以通过地址(指针)找到地址指向的对象。
上面代码就用了解引用操作符,*pa的意思就是通过pa中存放的地址,找到指向的空间,*pa其实就是a变量了;所以*pa=0,这个操作符是把a改成0。
这里的本质是将a的值交给pa来操作,这样对a的修改,我们就不知a=0这一种方法了,可以使我们的代码更加灵活多变。
2.3指针变量的大小
指针变量的大小取决于地址的大小
32位平台下的地址是32个bit位(即四个字节)
64位平台下的地址是64个bit位(即八个字节)
注意:指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。
3.指针变量类型的意义
3.1指针变量类型对指针解引用的影响
指针的类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节)。
比如:char*的指针解引用就只能访问一个字节,而int*的指针解引用就能访问四个字节。
3.2指针+-整数
我们先来观察一段代码
代码的运行结果如下图:
我们可以看出,char*类型的指针变量+1跳过1个字节,int*类型的指针变量+1跳过了4个字节。
这就是指针变量的类型差异带来的变化。
结论:指针的类型决定了指针向前走一步或者向后走一步有多大(距离)。
3.3void*指针
在指针类型中有一种特殊的类型是void*类型的,可以理解为无具体类型指针(或者叫泛型指针),这种类型的指针可以用来接受任意类型地址。但也有局限性,void*类型的指针不能直接进行指针的+-整数和解引用的运算。
例如:
可以看到将一个int类型的变量的地址赋值给一个char*类型的指针变量。编译器给出了一个警告,是因为类型不兼容。而使用void*类型就不会有这样的问题。
使用void*类型的指针接受地址:
注意看,void*类型的指针不能直接进行解引用操作,但是接收,存放的地址却没有任何问题。
void*类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果。使得一个函数来处理多种类型的数据。