9.3.5 指向多维数组的指针和指针变量探究
前面学习了用指针变量指向一维数组,其实指针变量也可以指向多维数组,但指向多维数组稍微难理解一点。
二维数组指针中的很多概念和一维数组中不一样,当然从常用和实用性来讲,二维数组指针用的也不多,建议有个印象,真要用的时候回头来再研究学习也不迟。例如,一维数组中a[i]表示一个值,但二维数组中a[i]表示的不是一个值,而是一个地址,这种情况比较多。
本节重点研究二维数组:看下面这个典型的二维数组,数组名a同样代表数组的首地址。
前面说过,可以把a看成是一个一维数组,这个一维数组有三个元素:a[0]、a[1]、a[2],每个元素又包含4个元素,那么听起来有如图9.21所示的这种感受。
图9.21 二维数组地址、元素感受图
考虑到二维数组理解上的一些复杂性,为了进一步明确二维数组地址、值等概念,笔者把一些最重要的内容整理成表9.1(横着看每一行),当某些表达式如何运算、结果是什么不确定的时候,直接查这个表格获取答案也许是最有效的学习和解决问题的方法。
表9.1 二维数组的一些表现形式及对应的含义
针对表9.1有几点说明:
(1)a是二维数组名,也是整个二维数组的首地址,针对该二维数组,所有针对行的描述全部从第0行开始,所有针对列的描述全部从第0列开始。所以,a可以认为是第0行的首地址(1000)。演示代码如下,可以自己设置断点跟踪调试一下:
(2)a+1、a+2分别代表第1行首地址和第2行首地址。第1行首地址,因为每一行包含4个元素,每个元素占4字节,所以4个元素占16字节,所以a+1要跳过16字节,也就是a+1=1016,而a+2要跳过32字节,也就是a+2=1032。演示代码如下:
(3)前面说过,把a看成是一个一维数组,这个一维数组有三个元素:a[0]、a[1]、a[2],每个元素又是一个包含4个元素的一维数组。
这表示a[0]、a[1]、a[2]是一维数组名,C语言规定数组名代表数组的首地址,所以就有如下:a[0]==&a[0][0]==1000是第0行首地址,a[1]==&a[1][0]==1016是第1行首地址,a[2]==&a[2][0]==1032是第2行首地址(注意==符号表示相等关系)。
演示代码如下:
(4)第0行第1列元素地址怎么表示?可以用&a[0][1],也可以用a[0]+1表示,因为a[0]是地址,所以+1跳过一个整型的4字节,所以a[0]+1=1004。举一反三,a[0]+2,a[1]+1就应该都会了。演示代码如下:
(5)回想讲解一维数组指针时,如一维数组“int a[5];”,回顾一下图9.20,从中看到a[0]和*a等价,a[1]和*(a+1)等价,可以推出a[i]和*(a+i)等价,这个推论拿到二维数组中同样适用,所以,在这里直接给出一个二维数组的结论:
a[0]等价于*a,注意,a和*a地址都是1000。
a[0]+1等价于*a+1等价于&a[0][1]等价于1004。
a[1]等价于*(a+1)等价于1016。
a[1]+2等价于*(a+1)+2等价于&a[1][2]等价于1024,注意不要把*(a+1)+2写成*(a+3),那就变成a[3]了。
(6)刚才推导了一下,这三项等价:a[0]+1、&a[0][1]、*a+1,代表第0行第1列元素地址,那么显然*(a[0]+1)就是a[0][1]的值,*(*a+1)也是a[0][1]的值。
也有:*(a[1]+2)、a[1][2]、*(*(a+1)+2)就是第1行第2列元素值的,这些听起来比较烦琐的内容,不用死记硬背,需要的时候看看表9.1就行了。
另外,针对a[i]的性质,再做一下进一步的说明:
如果a是一维数组名,那么a[i]代表的是a数组的第i个元素的内容。a[i]是有物理地址的,是占内存单元的。但如果a是二维数组,则a[i]代表的是一维数组名,这意味着a[i]本身并不占实际的内存单元,当然它也不会存放a数组中各元素的值,它只代表一个地址。所以,看表9.1:a、a+i、a[i]、*(a+i)、*(a+i)+j、a[i]+j都是地址,而*(a[i]+j)、*(*(a+i)+j)是二维数组元素a[i][j]的值。
另外,表9.1中也有一些比较有趣的内容值得关注:
①a和*a都是地址,而且这两个地址值是相同的。
②a+1和*(a+1),a+2和*(a+2)也是同样道理。
③a[i]和&a[i]都是地址,而且地址值也相同。
这些内容读者可以自己思考,并设置断点在计算机上执行程序观察结果,从而得出确切的结论。
说了这么多内容,可以进行一下实践了,试试用指针灵活地指向多维数组及其元素。看看如下范例: