对于绝大数初学者学习C语言最大的坎就是指针,不懂它的底层逻辑,其实指针不是高深语法,它本质和变量一样,只是存储的数据类型不同而已,而与指针有着直接关系的就是内存
什么是内存?(指针的根基)
一个通俗的比喻:比如把内存比作一整栋宿舍楼,每一个内存单元=独立房间,内存地址=房间门牌号,而内存是很大的,而我们常说的8G+256G,16G+512G,前面的8G和16G就是我们所说的内存也就是RAM
定义的变量如何标识它在内存当中的位置?
在C当中我们定义的变量都要存在内存当中,因此我们必须知道它存在哪里,所以这时候要给它分配空间,而这个空间的编号就是内存地址,所以计算机给每个位置通过地址进行定位
指针初阶
什么是指针变量?
说白了,指针变量就是一个变量而已,只不过是用来存储地址的变量,我们如果想要表示一个变量的地址,我们需要用"&"这个符号,这个符号是取地址的意思,用来取变量的地址
比如&a,这个就是存放变量a的地址,而int* p = &a,这个p就是一个整型指针变量,存放a的地址,我们定义指针就是这样定义
指针变量类型
我们都知道变量类型,比如char(占1个字节),short(占2个字节),int(占4个字节),float(占4个字节),而指针变量类型也是这几个,只不过定义方式不同,我们需要在此前期下加上个"*"这个符号,比如int* p,这就是一个整型指针变量,而定义指针变量类型,一般和变量类型保持一致即可,比如,int a = 10,int* p = &a,就可以这样定义
指针变量的大小
大多数的人可能认为指针变量的大小和指针变量的类型有关,比如我们定义int* p,按理说p的大小是4字节,其实这是错误的,指针变量的大小与指针类型是没有一点关系的,只与系统有关,比如系统是32位的,那么指针变量的大小就是4字节,如果是64位的,那么就是8字节
解引用
解引用就是我们直接通过指针访问变量,比如int a = 10; int* p = &a;我们都知道此时的p存放的是a的地址,并不是变量a的值(10),那么我们就可以用解引用的方法直接通过指针变量p来访问a的值(10),使用"*"这个符号,这时候我们打印*p就是10这个值
指针进阶
指针运算(+ – 整数)
说白了就是地址之间的运算,指针运算的本质和指针类型是有关系的,比如我们定义一个整型数组,数组之间的地址是连续的,因为是整型,所以每相邻两个数值之间大小是4个字节,如果此时我们让指针+1,则指针就会加4个字节,因此指针+1,加几个字节取决于指针的类型
大小端字节序
大小端是计算机系统中用于描述多字节数据在内存中存储顺序的概念,它决定了数据的高位字节和低位字节在内存中的物理排列顺序
大端序:高位字节存储在内存的低地址处
小端序:高位字节存储在内存的高地址处
常见架构:x86,x64架构通常使用小端序,网络字节序通常使用大端序
解引用访问进阶
由上面解引用介绍可知,解引用可以使我们直接通过指针来访问变量的值,但是如果我们此时定义的变量类型和指针变量类型是不相同的,那么我们此时直接通过指针来访问变量的值就可能行不通,比如下图的例子

首先,变量n是int型,4个字节,而在16进制中,1个字节就是两个16进制数,所以4个字节就是8个16进制数,而在内存中存储变量n有两种形式,就是上面图片所示,根据大小端字节序,可知变量n的存储方式是第二种,小端存储
所以说,根据两个代码,第一个代码运行的就是本身,而第二个运行结果是88,因为此时指针p是char类型,是一个字节,再加上是小端序,所以只会输出88,因此对于解引用来说,当前指针是什么类型的指针决定了你的访问能力
野指针
啥是野指针?
野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
成因
指针变量没有被定义,int* p;,这时候的p会被随机分配内存,这时候p就是野指针

越界访问了,比如访问数组越界

作用域超过了生命周期
怎么避免
一般当我们在不使用指针变量的时候,我们可以直接将其定义为NULL,这样不会被判定为野指针,比如int* p = NULL,但是当我们对p进行赋值的时候,指针变量指向的地址中的内容不会改变,因为指针变量定义的NULL中默认的地址是0,而地址在前几位是不允许被改变的
指针高阶
数组和指针
数组名表示数组首元素的地址,数组本质就是指针,指的是地址

可以看出,p,arr,&arr[0],这三个变量是一样的,都表示数组首元素的地址
但是,arr,&arr,这两个变量的含义是不同的,虽然可能生成的结果一样,其中arr表示的是数组首元素的地址,&arr表示的是整个数组的地址,含义是完全不一样的
数组关于指针一些常见问题
arr[1]与*(arr + 1)
比如我们在打印数组的值的时候,之所以我们直接用arr[下标]这样打印,是因为数组名就是首元素地址,数组下标语法本质就是地址偏移解引用,也就是说arr[1] = *(arr + 1)

用sizeof计算数组大小问题

从代码运行结果来看,输出结果完全不同,按理说应该是相同的,那是因为我们在计算数组大小的时候我们需要将定义的数组和sizeof放到一个生命周期里,而一旦不是放到一个生命周期里,此时计算的就是指针变量本身的大小,而至于其大小我们上面已说明,因此会导致不同的结果
二级指针
二级指针本身就是套娃,二级指针主要作用就是存储一级指针的地址,对二级指针进行一次解引用,实际就是获取到一级指针存储的内容,也就是变量的地址,而对二级指针进行两次解引用,得到的就是变量的内容


可以看出上面的图就可以很好的解释二级指针
指针数组
指针数组就是存放指针的数组,就是数组

可以看出定义指针数组是怎么定义的
数组指针
本质是指针,指向数组的指针
什么是指向数组的指针?
什么是指向整型的指针?
而我们可以根据第三个问题回答第一个问题,int a = 10; int* p = &a; 此时的p就是指向整型的指针,而数组指针的定义是数据类型 (*指针名)[数组长度]; 比如int(*p)[4],此时(*p)表示p是一个指针,此时这个指针指向的长度为4的数组,这个数组是int类型的数组,而这就是数组指针



