数组和指针
今天花了一晚上时间重新回顾了数组和指针的关系,特地整理出来放在这里。
1:数组名和指针。
数组名到底是什么?这是一个值得探讨的问题。我们知道一维数组名的内容是该数组首元素的地址。
举个例子:定义int a[3];然后分别输出a和&a[0],内容是一样的。
我们在引入一个概念:数组的地址。什么是数组的地址?从内容上看数组的地址就是数组名的内容,也就是首元素的地址。仍然使用之前的那个例子,输出&a和a,内容是一样的。
那么这两者的数据类型是什么呢?
这时候我们就不能从本质上理解了,按道理,a是int *类型,那么&a应该是int **类型吧?
然而事实并非如此。C++编译器对数组的地址进行了特殊的处理,将&a定义为了int (*p)[3]类型。
那么int (*)[3]和int *到底有啥区别?
就以&a和a为例:&a++指针移动的位置是3个int单位,而a++移动的是一个int单位。
编译器的潜在意思就是&a是整个数组的地址,而a只是一个元素的地址。
这在多维数组中具有很大的意义。我们先以二维数组为例。int a[3][3],其数组名a = &a[0](这一点要记清楚,无论几维数组他的数组名都是其首元素的地址,二维数组的首元素是一个一维数组,因此他的数组名就是一个一维数组的地址,或者说是指向一个一维数组的指针),a的类型就是int(*)[3]。假设我们创建一个int (*p)[3],就可以令p保存二维数组的数组名,可以利用p来操作数组了。(p还是指向一维数组的指针)但本质上讲并不能说p指向了二维数组,只是C++对数组名的特殊处理机制(对[]运算符的定义,利用[]可以指定指针移动的单位)使得我们可以利用它来访问整个二维数组(就好比在一维数组里,你得到了首元素的地址就可以利用它来访问每一个元素了,本质上是利用该元素地址的移动,表面上看则是利用[]索引来实现)。这时候我们令p加1就可以访问下一个元素(就是访问下一行啦)。
2.数组名和函数形参
理解了之前的内容之后下面的东西就不难理解了:C++中,当你把数组名作为形参的时候,传过去的就是首元素的地址。也可以这么理解,数组名退化为上一维的指针。比如一维数组名退化为指向单个元素的指针,二维数组名退化为指向一维数组的指针。。。等等
3.用指针操作多维数组
①使用指向单个元素的指针
需要明白的是,在内存角度看,无论几维数组都是线性、一维储存的。因此只要知道第一个元素的地址就能一路顺到最后一个元素。
以二维数组为例:int a[2][2],只要获取了a[0][0]的地址就可以访问所有a中的元素。访问a[i][j]就是 *(a+i*2+j),事实上编译器在处理[]时就是这么处理的。
所以你可以利用一个int *来操作任何维数的int数组。
②使用指向下一维度的指针
换句话说就是直接传数组名了,参照上面的内容很容易就能理解。
③使用多级指针
这个常用于动态分配出的数组。使用起来也十分容易理解。
4.小结
归纳一下主要的几个概念:
整个数组的地址
C++对[]的处理//其实原理就在这里,只是因为被隐藏了所以看不懂
数组名和数组的地址
指向数组的指针
5.引入一张令我茅塞顿开的C++primerplus上的图吧