指针可以指向复合类型,上一节讲了指向指针的指针,这一节学习指向数组的指针。以下定义一个指向数组的指针,该数组有10个int
元素:
int (*a)[10];
和上一节指针数组的定义int *a[10];
相比,仅仅多了一个()
括号。如何记住和区分这两种定义呢?我们可以认为[]
比*
有更高的优先级,如果a
先和*
结合则表示a
是一个指针,如果a
先和[]
结合则表示a
是一个数组。int *a[10];
这个定义可以拆成两句:
typedef int *t; t a[10];
t
代表int *
类型,a
则是由这种类型的元素组成的数组。int (*a)[10];
这个定义也可以拆成两句:
typedef int t[10]; t *a;
t
代表由10个int
组成的数组类型,a
则是指向这种类型的指针。
现在看指向数组的指针如何使用:
int a[10]; int (*pa)[10] = &a;
a
是一个数组,在&a
这个表达式中,数组名做左值,取整个数组的首地址赋给指针pa
。注意,&a[0]
表示数组a
的首元素的首地址,而&a
表示数组a
的首地址,显然这两个地址的数值相同,但这两个表达式的类型是两种不同的指针类型,前者的类型是int *
,而后者的类型是int (*)[10]
。*pa
就表示pa
所指向的数组a
,所以取数组的a[0]
元素可以用表达式(*pa)[0]
。注意到*pa
可以写成pa[0]
,所以(*pa)[0]
这个表达式也可以改写成pa[0][0]
,pa
就像一个二维数组的名字,它表示什么含义呢?下面把pa
和二维数组放在一起做个分析。
int a[5][10];
和int (*pa)[10];
之间的关系同样类似于int a[10];
和int *pa;
之间的关系:a
是由一种元素组成的数组,pa
则是指向这种元素的指针。所以,如果pa
指向a
的首元素:
int a[5][10]; int (*pa)[10] = &a[0];
则pa[0]
和a[0]
取的是同一个元素,唯一比原来复杂的地方在于这个元素是由10个int
组成的数组,而不是基本类型。这样,我们可以把pa
当成二维数组名来使用,pa[1][2]
和a[1][2]
取的也是同一个元素,而且pa
比a
用起来更灵活,数组名不支持赋值、自增等运算,而指针可以支持,pa++
使pa
跳过二维数组的一行(40个字节),指向a[1]
的首地址。