指针的本质:存地址的变量
指针的定义:变量类型 + * + 变量名
例子:
//这里以整形定义指针为例
#include<stdio.h>
int main()
{
int a = 10; //定义变量a,并对其初始化,值为10
int* p; //定义指针p
p = &a; //把a的地址赋给p,此时p就指向了a
//&为取地址符号,可以找出变量地址
//即 *p = a;
//调用指针时变量名前加*,为间接引用,也称作解引用
printf("%d",*p); //调用一下*p我们会发现输出结果为10
return 0;
}
调用指针时变量名前加*,为间接引用,也称作解引用
如果不懂下翻有图示见二级指针前
根据例子我们来简单形容一下:
首先定义了一个变量 a,值为10
又定义了一个指针p,p为a的地址
(我们知道,当变量定义(声明)出来后,是占用空间的,
这个空间所在的位置就是这个变量的地址)
假设a的地址为0x10
所以p的值是0x10
注意:p是指针,存放地址,而不是p的地址,p这块空间专门存放地址,也就是回到了指针的本质,它是储存地址的变量
我们发现在定义指针时需要加 *
那么p这个指针的 数据类型 为int*型,不要把int与*拆开记忆
而p的 指针指向类型 则为int型
变量的类型
去掉变量名即为 变量类型
例:int* p; //则变量p的类型为int*型
int** p; //则变量p的类型为int**型
由此可见,即使这个类型我们并不知道他叫什么,但是通过这样的方式我们便可以直接知道变量的类型
指针指向的类型
去掉变量名和一个*即为 指针指向的类型
例:int* p; //指针指向的类型为int型
int** p; //则变量p的类型为int*型
同理,即使这个类型我们并不知道他叫什么,但是通过这样的方式我们便可以直接知道指针指向的的类型
下面这是一个指针的示意图

空指针:指向空的指针
例:int* p = NULL;
野指针:无固定指向的指针
即指针未初始化,尽量避免使用
二级指针
定义方式:变量类型 + ** + 变量名 //注意此处变量类型中不要带*号 //例如已经是int*型再加*,即可变为二级指针
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a; //p这块空间是存放地址a的,存在空间
//只要是空间就一定存在地址
//而单看a的地址不占空间
int** p1 = &p; //此时p1指向p
printf("%p\n",p1); //这时我们得到的是p1的值,也就是p的地址
printf("%p\n", *p1); //p1进行一次间接引用,得到的就是p的值,也就是a的地址
printf("%d\n", **p1); //p1进行两次间接引用,得到的就是a的值,10
return 0;
}
此处就不再进行画图解释,如果不懂的小伙伴可以私信我哦~
指针数组与数组指针
数组
首先我们要知道数组的形式
即:类型+数组名+[大小]
例:
int a[10]; //开辟一个为10个int型大小的连续空间
其中,数组名代表数组中首元素的地址
根据例子即是:a即是a[0]的地址
那么中括号的作用是什么呢?
答:先偏移n位,再间接引用
那么我们再来举个例子:
int a[10];
a[0] = 0; //先对首地址向后偏移0位,再对其间接引用,得到的元素将其赋值为0
a[1] = 1; //先对首地址向后偏移1位,再对其间接引用,得到的元素将其赋值为1
//依次类推
指针数组的定义
首先我们来看一下的运算符优先级表,对我们接下来的定义会有帮助
运算符优先级表
| 运算符 | 结合性 |
| () [] -> . (前述运算符) | 自左向右 |
|
! ~ ++ -- - (type) * & sizeof (单目运算符) | 自右至左 |
| * / % (算数运算符) | 自左向右 |
| + - (算数运算符) | 自左向右 |
| << >> (移位运算符) | 自左向右 |
| < <= > >= (关系运算符) | 自左向右 |
| == != (关系运算符) | 自左向右 |
| & (逻辑运算符) | 自左向右 |
| ^ (逻辑运算符) | 自左向右 |
| | (逻辑运算符) | 自左向右 |
| && (逻辑运算符) | 自左向右 |
| || (逻辑运算符) | 自左向右 |
| ?: (条件运算符,三目运算符) | 自右至左 |
| assignments (赋值运算符) | 自右至左 |
| , (逗号运算符) | 自左向右 |
由表中我们可以看出 [ ] 的优先级高于 *,故变量名一定会优先与 [ ] 结合,而 * 则会由于自右至左先与类型结合
举个栗子:
int arr[10] = {0};
int* p[10]; //数组每个元素都是int*类型
p[0] = &arr[0];
p[1] = &arr[1];
...
这就是指针数组
我们会发现,这样的数组内部每个元素皆是一个int*类型的元素,也就是说,每个元素都是一个指针,但是初始化或赋值会很麻烦,我们需要自己不断地给每个元素分配地址
那怎么办呢?
数组有这样一个规则:数组虽然是一块连续的空间,每块小空间有各自的地址,但我们可以取出数组的整块地址,如下:
数组整个地址:&+数组名
从数值上看:数组地址&arr = 数组首地址arr = 数组首元素地址&arr[0]
但数组地址&arr+1,会偏移到下一个数组地址
而数组首元素地址arr+1,会偏移到下一个元素
那么我们对数组地址&arr间接引用便会得到数组首元素地址arr
int arr[10]; //该数组的整个地址则为:&arr
知道了整个地址之后,我们便可以解决指针数组初始化麻烦的问题,但这是我们将用的就是数组指针
数组指针的定义
与指针数组不同的是,它的所有元素可以都不是指针
因为我们数组指针要指向的就是被指向数组的整个地址
这时我们面临的问题就是,怎样定义才可以指向整个地址呢?
举个栗子:
int arr[10];
int(*p)[10] = &arr;
这样我们会发现,不同于指针数组,我们将*与变量名用小括号括在一起,因为小括号优先级最高,所以我们会发现,数组指针定义的意思是,先告诉我们*p是一个指针,指向的是元素都为int型长度为10的数组,此时,我们发现它就是指向整个数组的指针,即为数组指针
此时的数组指针里元素与arr中都相同,而指向的则是整个arr数组
那么我们如何调用数组指针里的元素呢?
根据前面我们讲的中括号的作用是先偏移n位,再间接引用以及对数组地址&arr间接引用便会得到数组首元素地址arr,比如在输出的时候:
printf("%d\n", (*p)[i]); //i为你想要输出第几位元素
我们便可以执行这一行语句,*p是先间接引用,对整个数组地址间接引用得到首元素地址,在偏移i位,再进行一次间接引用则可以从地址调用到数组里的第i位元素
总结
一.注意避免使用野指针
二.数组指针与指针数组的本质区别
数组指针指向的是整个数组地址,内部元素均与指向的数组元素类型相同
指针数组内部每个元素都是指针,需自己分配每个指针指向哪里
——————————————
后记:
以上就是我所总结的内容,当然指针这一部分还有很多广泛应用,例如链表,函数指针等等
同时如果我有打错的地方也欢迎各位大佬私信评论纠正哦~
如果你喜欢这片文章或者觉得有用麻烦给个关注和点赞呗,谢谢~
后续我会继续更新c++,c语言方面的知识总结,欢迎各位催更AwA
本文详细解析了指针的概念,包括其本质(存储地址的变量)、定义(变量类型+*+变量名)、不同类型指针的使用,以及野指针、二级指针、数组和数组指针的区别。强调了避免野指针的重要性,并以C语言为例进行说明。

630

被折叠的 条评论
为什么被折叠?



