C语言的指针与多维数组( 二 )


下图以另一种视图演示了数组地址、数组内容和指针之间的关系 。

C语言的指针与多维数组

文章插图
An array of arrays.
指向多维数组的指针如何声明一个指针变量pz指向一个二维数组(如,zippo)?在编写处理类似zippo这样的二维数组时会用到这样的指针 。把指针声明为指向int的类型还不够 。因为指向int只能与zippo[0]的类型匹配,说明该指针指向一个int类型的值 。但是zippo是它首元素的地址,该元素是一个内含两个int类型值的一维数组 。因此,pz必须指向一个内含两个int类型值的数组,而不是指向一个int类型值,其声明如下:
int (* pz)[2];// pz points to an array of 2 ints以上代码把pz声明为指向一个数组的指针,该数组内含两个int类型值 。为什么要在声明中使用圆括号?因为[]的优先级高于* 。考虑下面的声明:
int * pax[2];// pax is an array of two pointers-to-int由于[]优先级高,先与pax结合,所以pax成为一个内含两个元素的数组 。然后*表示pax数组内含两个指针 。最后,int表示pax数组中的指针都指向int类型的值 。因此,这行代码声明了两个指向int的指针 。而前面有圆括号的版本,*先与pz结合,因此声明的是一个指向数组(内含两个int类型的值)的指针 。程序zippo2.c演示了如何使用指向二维数组的指针 。
/* zippo2.c --zippo info via a pointer variable */#include <stdio.h>int main(void){int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5, 7} };int (*pz)[2];pz = zippo;printf("pz = %p,pz + 1 = %pn",pz,pz + 1);printf("pz[0] = %p, pz[0] + 1 = %pn",pz[0],pz[0] + 1);printf("*pz = %p,*pz + 1 = %pn",*pz,*pz + 1);printf("pz[0][0] = %dn", pz[0][0]);printf("*pz[0] = %dn", *pz[0]);printf("**pz = %dn", **pz);printf("pz[2][1] = %dn", pz[2][1]);printf("*(*(pz+2) + 1) = %dn", *(*(pz+2) + 1));return 0;}下面是该程序的输出:
: pz = 0x7ffc3a82bc60,pz + 1 = 0x7ffc3a82bc68: pz[0] = 0x7ffc3a82bc60, pz[0] + 1 = 0x7ffc3a82bc64:*pz = 0x7ffc3a82bc60,*pz + 1 = 0x7ffc3a82bc64: pz[0][0] = 2:*pz[0] = 2:**pz = 2:pz[2][1] = 3: *(*(pz+2) + 1) = 3系统不同,输出的地址可能不同,但是地址之间的关系相同 。如前所述,虽然pz是一个指针,不是数组名,但是也可以使用pz[2][1]这样的写法 。可以用数组表示法或指针表示法来表示一个数组元素,既可以使用数组名,也可以使用指针名:
: zippo[m][n] == *(*(zippo + m) + n): pz[m][n] == *(*(pz + m) + n)指针的兼容性指针之间的赋值比数值类型之间的赋值要严格 。例如,不用类型转换就可以把int类型的值赋给double类型的变量,但是两个类型的指针不能这样做 。
int n = 5;double x;int * p1 = &n;double * pd= &x;x = n;// implicit type conversionpd = p1;// compile-time error更复杂的类型也是如此 。假设有如下声明:
int * pt;int (*pa)[3];int ar1[2][3];int ar2[3][2];int **p2;// a pointer to a pointer有如下的语句:
pt = &ar1[0][0];// both pointer-to-intpt = ar1[0];// both pointer-to-intpt = ar1;// not validpa = ar1;// both pointer-to-int[3]pa = ar2;// not validp2 = &pt;// both pointer-to-int **p2 = ar2[0];// both pointer-to-intp2 = ar2;// not valid注意,以上无效的赋值表达式语句中涉及的两个指针都是指向不同的类型 。例如,pt指向一个int类型值,而ar1指向一个内含3个int类型元素的数组 。类似地,pa指向一个内含3个int类型元素的数组,所以它与ar1的类型兼容,但是ar2指向一个内含2个int类型元素的数组,所以pa与ar2不兼容 。
上面的最后两个例子有些棘手 。变量p2是指向指针的指针,它指向的指针指向int,而ar2是指向数组的指针,该数组内含2个int类型的元素 。所以,p2和ar2的类型不同,不能把ar2赋给p2 。但是,*p2是指向int的指针,与ar2[0]兼容 。因为ar2[0]是指向该数组首元素(ar2[0][0])的指针,所以ar2[0]也是指向int的指针 。
一般而言,多重解引用让人费解 。例如,考虑下面的代码:
int x = 20;const int y = 23;int * p1 = &x;const int * p2 = &y;const int ** pp2;p1 = p2;// not safe -- assigning const to non-constp2 = p1;// valid-- assigning non-const to constpp2 = &p1; // not safe -- assigning nested pointer types前面提到过,把const指针赋给非const指针不安全,因为这样可以使用新的指针改变const指针指向的数据 。编译器在编译代码时,可能会给出警告,执行这样的代码是未定义的 。但是把非const指针赋给const指针没问题,前提是只进行一级解引用:


推荐阅读