指针与地址——从定义到应用

指针的本质:存地址的变量

指针的定义:变量类型 + * + 变量名

例子:

//这里以整形定义指针为例

#include

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*型

同理,即使这个类型我们并不知道他叫什么,但是通过这样的方式我们便可以直接知道指针指向的的类型

下面这是一个指针的示意图

指针与地址——从定义到应用_第1张图片

空指针:指向空的指针

例:int* p = NULL;

野指针:无固定指向的指针

即指针未初始化,尽量避免使用

二级指针

定义方式:变量类型 + ** + 变量名        //注意此处变量类型中不要带*号                                                                                                //例如已经是int*型再加*,即可变为二级指针

#include

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

//依次类推

指针数组的定义

首先我们来看一下的运算符优先级表,对我们接下来的定义会有帮助

运算符优先级表

C语言运算符优先级表(由上至下,优先级依次递减)
运算符 结合性
()    []     ->    .                                        (前述运算符) 自左向右

!  ~    ++    --    -     (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语言,c++)