进阶指针
导言
大家好,很高兴有和大家见面啦!
经过前面的学习,大家现在对指针的内容应该有了一个初步的印象。为了帮助大家将指针的知识点好好的消化吸收,今天开始我们将对指针的内容进行深入的探讨。下面我们就开始今天的内容吧!
九、二级指针与指针数组
通过前面的介绍,我们知道,对于一维数组来说,一维数组的数组名在除了计算数组所占空间大小以及进行取地址操作这两种情况外,数组名就等价于数组首元素的指针。 大家现在思考一下,如果我们将一维数组的元素替换成指针类型,那与这个数组名等价的数组首元素的指针又应该是什么样的呢?
为了搞清楚这个问题,下面我们来分析一下;
指针存放的是数组首元素的地址,此时的数组首元素的类型不是指针类型; 当数组首元素的类型为指针类型时,此时存放首元素地址的指针存放的应该是一个指针的地址;
在前面的介绍中我们有提到过,存放指针地址的指针我们将其称为二级指针。也就是说指针数组首元素的指针是一个二级指针,根据指针与数组的关系我们可以得到:
- 在除了计算指针数组所占内存空间大小以及进行取地址操作这两种情况外,指针数组的数组名等价于一个二级指针;
有了这个结论后,下面我们来探讨一下对于二级指针与指针数组我们应该如何使用;
9.1 二级指针+-整数
对于一级指针来说,指针+-整数得到的值是指针+-对应数据类型所占空间大小*整数。那二级指针进行加减整数时又会是什么情况呢?下面我们就来测试一下:
从测试结果中我们可以看到,对于不同类型的二级指针来说,在进行加1或者减1时,变化的大小都是4个字节。既然这样,那我们就用char**
的指针ppa
为例子来继续测试加减整数:
可以看到二级指针ppa在进行加减整数时,指针变化的值为4的整数倍。
通过这两次的测试结果,我们就能得到以下结论:
- 不同类型的二级指针进行加1和减1后,指针变化的值都为4;
- 同一类型的二级指针进行加减整数后,指针变化的值为4的整数倍;
此时有朋友可能就会好奇了,为什么是4这个值呢?我们先来回顾一下指针加减整数的规则:
下面我们来看一下一级指针的创建格式:
//一级指针的创建格式
type* p = &name;
//type——指针对应的数据类型
//*——表示此时的变量为指针类型
//p——指针变量名
//name——被指针指向的变量
从这个格式中,我们可以看到,在type*
中,type
表示的就是指针对应的数据类型;
前面我们在介绍二级指针时,我们是将其中一颗*
给了指针变量也就是type* *p = &p;
的格式。下面我来给大家介绍一个新的思路,我们将二级指针看做一个一级指针,然后我们就能得到二级指针的格式:
//二级指针的创建格式
(type*)* pp = &p;
//type*——指针对应的数据类型
//*——表示此时的变量为指针类型
//pp——指针变量名
//p——被指针指向的变量
可以看到,在这种格式下,我们就可以很清楚的看到,二级指针对应的数据类型为指针类型。在前面我们也介绍过,对于不同的指针类型在32位操作系统下所占内存空间大小都是4个字节,在64位操作系统下所占内存空间大小都是8个字节。而前面我们是在32为操作系统下进行测试的,这就是为什么对于二级指针进行+-整数时变化的值为4的整数倍。
9.2 二级指针-二级指针
对于一级指针来说,在数组中,指针-指针的值为指针之间的元素个数。二级指针也满足这个运算规则,如下所示:
我们如果将二级指针看做一级指针的话,那这里就相当于是一个指针对应的数据类型为整型指针类型的两个一级指针在进行相减,所得的结果也是两个一级指针之间的元素个数。
9.3 二级指针的关系运算
同理,对于指针指向的目标为指针类型的一级指针,它也是可以进行关系运算的。如下所示:
现在大家应该都能理解二级指针和指针数组之间的关系了。与其说是二级指针与指针数组之间的关系,倒不如说是一级指针与移位数组之间的关系,只不过此时的指针所指向的元素的数据类型为指针类型,数组元素的数据类型也为指针类型;
可以看到,此时对数组名进行解引用操作也是可以像指针一样找到数组各个元素的,不管是整型数组也好还是整型指针数组也好,都是能够正常访问的;
- 使用
指针[下标]
来访问数组元素;
可以看到,此时对指针使用下标引用操作符也是能够正常访问数组的各个元素的,不管是整型一级指针还是整型二级指针,都是可以正常访问的;
- 进行数组传参时,通过指针接收;
通过指针来接收数组的传参,是没有任何问题的,这里我们可以看到指针接收完后,还能通过下标引用操作符来找到数组的各个元素;
- 进行指针传参时,通过数组接收
通过数组来接收指针的传参也是没有任何问题的,这里我们看到数组在接收完后,还能通过解引用操作符来访问指针指向的各个对象;
经过上面的例子,我相信大家对指针与数组名之间的相互转换已经非常熟悉了,下面我给大家介绍一些关于指针与数组之间的有趣的变形;
现在咱们已经将二级指针与指针数组之间的关系介绍完了。不知道有没有朋友会有疑惑,我们这里一直在强调除了用sizeof计算数组所占空间大小和通过取地址操作符取出数组的地址这两种情况外,其他的时候指针与数组名是等价的,那它们之间可不可以进行相互转换呢?下面我们就来探讨一下;
9.4 指针与数组名的相互转换
为了探讨指针与数组名能否进行相互转换,下面我们先进行第一个测试:
- 使用
*(数组名+下标)
来访问数组元素;
可以看到,此时对数组名进行解引用操作也是可以像指针一样找到数组各个元素的,不管是整型数组也好还是整型指针数组也好,都是能够正常访问的;
- 使用
指针[下标]
来访问数组元素;
可以看到,此时对指针使用下标引用操作符也是能够正常访问数组的各个元素的,不管是整型一级指针还是整型二级指针,都是可以正常访问的;
- 进行数组传参时,通过指针接收;
通过指针来接收数组的传参,是没有任何问题的,这里我们可以看到指针接收完后,还能通过下标引用操作符来找到数组的各个元素;
- 进行指针传参时,通过数组接收
通过数组来接收指针的传参也是没有任何问题的,这里我们看到数组在接收完后,还能通过解引用操作符来访问指针指向的各个对象;
经过上面的例子,我相信大家对指针与数组名之间的相互转换已经非常熟悉了,下面我给大家介绍一些关于指针与数组之间的有趣的变形;
9.5 有趣的变形
- 下标[数组名]
可以看到对于下标引用操作符,下标与数组名的位置是可以进行互换的;
- 下标[指针]
既然数组名就是指针,那么对于指针来说,在使用下标引用操作符时也是同样可以将指针与下标的位置进行互换的;
- &指针[下标]
当我们对指针使用下标引用操作符时,就等价于对指针进行解引用,再进行解引用后再对其取地址,还能得到指针;
- *数组名
我们可以通过对数组名进行解引用来访问数组的各个元素,但是,数组名并不能像指针一样进行自增操作:
这里是因为对于数组名来说,数组名是整个数组在内存中的起始位置,此时如果对数组名进行自增,就相当与是把整个数组的起始位置进行移动,所以此时的数组名表示的也是整个数组,并不是数组的首元素,这个一定要牢记;
9.6 总结
介绍到这里,关于指针与数组的关系我们就全部介绍完了,下面我们来对这些内容做个总结:
- 有三种情况数组名表示的是整个数组:
- 通过
sizeof
计算数组所占空间大小; - 使用取地址操作符&对数组名进行取地址操作;
- 对数组名进行自增自减操作等赋值操作;
- 在其它情况下,数组的本质就是指针:
;
;
- 我们可以修改指针指向的内容,但不能修改数组名指向的内容;
即:我们可以对指针进行自增/自减等赋值操作,但不能对数组名进行自增自减等赋值操作;
十、指针数组模拟二维数组
在搞清楚了指针与数组的关系后,我们再来探讨一下指针数组。
既然数组的本质就是指针,而对指针数组来说,数组元素为指针,也就是说我可以认为指针数组的元素可以是数组。
在前面介绍二维数组时,我们有介绍过一种理解——二维数组可以看做是一维数组的集合。
既然指针数组的元素可以是数组,而二维数组可以看做是一维数组的集合,那是不是说指针数组与二维数组等价呢?
很遗憾的告诉各位,指针数组与二维数组并不等价,同理二级指针与二维数组也是不等价的。之所以不等价,是因为对于指针数组来说,数组元素存放的地址可以是不连续的地址,但是对于二维数组来说,数组元素的地址是连续存放的,如下所示:
从结果中我们可以看到:
- 对于二维数组的元素地址间的差值刚好是8,也就是说,二维数组可以看做是三个地址连续存放的一维数组的集合;
- 而对于指针数组来说,我们可以看到指针数组的元素的地址之间并不相邻;
因此指针数组与二维数组并不等价,又因为指针数组与二级指针等价的,所以二级指针与二维数组并不等价;
既然我们从地址连续存放的一维数组的集合的角度来看待二维数组的话,那我们就可以通过指针数组来模拟实现二维数组。如下所示:
在前面对指针与数组的探讨中我们得到的结论是在对数组元素进行访问时解引用操作符*<==>下标引用操作符[]
。因此我们可以通过第二个下标引用操作符来访问指针数组中各元素的数组元素。
结语
对于指针数组的内容我们先介绍到这里,今天的这些内容可谓是干货中的干货,大家一定要好好消化一下哦!!!
在下一篇内容中,我们会对指针的内容继续深入,大家记得关注哦!!!
最后,感谢各位的翻阅,咱们下一篇再见!!!