上文我们已经学习了关于指针的比较浅层的知识,但是学习不可以浅尝辄止,那么接下来,我们就要对指针进行深一步的了解。
一.数组名的理解
数组名其实就是数组首元素的地址,我们可以来看下面这个代码:
我们可以发现,打印出来的两个值是一样的。
那么既然数组名是数组首元素的地址,那么下面这个代码该作何解释呢:
如果这里的arr代表的是数组首元素的地址,那结果应该是4/8(指针的大小,详细解释见文章指针(一))才对,但却输出了40, 实际上,关于数组名的理解有两种特殊情况:
(1)sizeof(arr),这里的数组名代表的是整个数组,那么数组的每一个元素都是整型,10个整型的大小是40个字节。
(2)&arr,这里代表的是取出整个元素的地址
那么,我们再看下面这个代码:
我们会发现,三个打印的结果是一样的!那么有的人可能就又不解了,arr与&arr又有什么区别呢?
我们不妨运行一下下面的代码,或许你会找到答案:
通过观察运行结果,我们发现,arr与arr+1相差四个字节,&arr[0]与&arr[0]+1相差四个字节,这是因为arr与&arr[0]都是首元素的地址,+1则跳过一个整型数组元素,也就是四个字节;而&arr与&arr+1相差40个字节,也就是跳过了一个数组,从而印证了&arr其实取出的是整个数组的地址。
二.使用指针访问数组
通过以上知识的学习,相信大家如果用指针访问数组应该不成问题吧!
那么我们直接来试试吧:
我们不妨再做深一步的思考,在这个代码中,数组名arr与p是等价的,我们既然可以用arr[i]访问数组元素,那么p[i]是否也可以呢?不如动手实践一下呢!
果然不出所料。实际上,*(p+i)等价于p[i],*(arr+i)等价于arr[i],数组元素在被访问时,是首元素地址加上偏移量求出元素地址,再被解引用,从而访问的。
三.一维数组传参的本质
我们发现,再32位平台下,在test函数中,sz的值为4,这说明数组传参传进去的不是整个数组,而是数组首元素的地址。因此我们得出结论:数组传参本质上传的是数组数组首元素的地址。那么函数形参部分理论上应该用指针来接收地址,但是我们需要注意,虽然在这个代码中我们使用了数组形式,但它本质上还是指针。所以一维数组传参,形参部分可以写成数组的形式,也可以写成指针的形式。
四.二级指针
指针变量也是变量,也需要存储空间,也有自己的地址,那么它的地址用谁来存放呢——二级指针。我们可以看下面这个代码:
这样我们就创建了二级指针变量。
那么二级指针变量解引用如何理解呢?
*ppa只解引用一次,得到的是pa这个指针;而**ppa解引用得到的是a这个变量。
五.指针数组
有些同学可能对指针数组很陌生,很疑惑,他到底是指针还是数组呢?我们不妨类比一下
整型数组就是一种存放整型的数组,字符数组就是一种存放字符的数组,那么以此类推,指针数组当然就是一种存放指针的数组。
如下图:
指针数组每一个元素又是一个指针,指针又可以指向地址。
六.指针数组模拟二维数组
我们已经知道,指针数组是一种存放指针的数组,而指针又可以指向一块位置。那么,我们能否用指针数组模拟二维数组呢?我们不妨上手操作一下:
p[i]就是指针数组的元素,也就是数组名(arr1,arr2,arr3),那么p[i][j]就可以访问到数组中每一个元素。
当然还有第二种写法:
上述代码模拟出二维数组的效果,但它们实际上并非完全是二维数组,因为他们的每一行并不是连续的。
通过上文的学习,相信你对指针一定有了更深的理解,下期我们将会对指针部分的知识进行更深一步的剖析,我们下期再会!