在学习C语言的过程中,多维数组的指针是一个难点,许多初学者接受起来都感到困难,特别是对二维数组中a+i=a[i]=*(a+i)这样的等式不能理解,而在应用中对“行指针”和变量指针的运用也经常出现错误。纠其原因,主要是没有抓住问题的关键,同时又受到一维数组思维惯性的影响,不能从较高的角度来掌握问题的本质。笔者总结多年讲授C语言的教学经验,提出用“级别论”的方法来分析问题,收到了较好的效果,能够帮助初学者加深对这一问题的理解。
1 数组地址的“级别”1.1 什么是数组地址的“级别”?
为了有助于对问题的理解,我们人为地给数组的地址“定级”,以二维数组为例,已知有如下说明语句:
static int a[3][4];
则a+i、a[i]、&a[i][0]都表示地址,且有a=a[i]=&a[i][0],它们的区别是什么?我们说,a是“二级地址”,它是指向“行”的;a[i]是“一级地址”,它是指向元素的。归纳起来我们有如下论断:
二维数组名+i=“二级地址”,它指向数组中的某一行,称为“行地址”;
一维数组名+i=“一级地址”,它指向数组中的某一个元素,称为“元素地址”或“点地址”;
作为特例,我们可以把数组元素称为“0级地址”。
1.2 各级地址的取值范围
二级地址的取值范围是各行的首地址,亦即每一行第一个元素的地址,因此有a+i=&a[i][0];而一级地址的取值范围是所有元素地址值,因此有a[i]+j=&a[i][j]。由此可以看出,一、二级地址的值域有相交的部分。当一、二级地址都取某一行的首地址时,它们的值便相等了。此时便有:
a+i=a[i]=*(a+i)=&a[i][0]。
1.3 推广到一般情况
对于三维数组,我们有:
三维数组名+i=“三级地址”,它指向数组中的某一平面,称为“层地址”;
二维数组名+i=“二级地址”,它指向数组中的某一直线,称为“行地址”;
一维数组名+i=“一级地址”,它指向数组中的某一个元素,称为“点地址”。
例如int
a[3][4][5];是一个三维数组,则a表示三级地址,a[0]表示二级地址,a[0][0]表示一级地址,而a[0][0][0]则表示元素(0级地址)。
用此方法,我们可以推广到n维数组,n维数组中的最高级地址是“n级地址”。当然超过三维时,我们便难以给出它的几何解释。2 运算符*与[]的作用在表示多维数组的指针时,数组名与指针运算符*和下标运算符[]紧密的结合在一起,此时两者的作用是等价的,它们都起到“降级”的作用。若a表示二级地址,则a[0]和*a表示一级地址,a[0][0]和**a则表示元素。由此可以看出,每增加一个*或[],地址的级别就下降一级,在这里*和[]可以混合使用,例如*(a[i]+j
),*(*(a+i)+j),
a[i][j]都表示二维数组中第i行第j列的元素。判断一个指针常量或变量的“级别”只需查看表达式中*与[]的个数便一目了然了。3 指针变量的“级别”与指针常量(地址)相对应指针变量也有它的“级别”。指针变量的级别是由其定义方式决定的。
3.1 设p为指针变量,若其定义语句为int
(*p)[n]则p为二级指针变量,它的初值也只能是二级地址,此时p+1指向下一行,亦即“二级指针按行移动”。
例1、输出二维数组任一行任一列元素的值
main ()
{ static int
a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int i,j,(*p)[4];/* 定义二级“行指针”p */
p=a; /*二级指针变量的初值为二级地址 */
scanf (″i=%d, j=%d ″, &i, &j);
printf (″a[%d, %d]=%d\n ″,i,j,*(p+i)+j));
}
运行结果:
i=1, j=2
a[1,2]=13
3.2 设p为指针变量,若其定义语句为 int
*p;则p为一级指针变量,它的初值也只能是一级指针,此时p+1指向“下一个元素”,亦即一级指针按元素移动。
例2、用指针变量输出数组元素的值。
main ()
{ static int
a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int i, (*p) /* 定义一级p */
p=a[0]; /*一级指针变量的初值只能是一级地址 */
for (p=a[0]; p
{if ((p-a[0])%4==0) printf (″\n ″)
printf (″%4d ″, *p);
}
}
运行结果:
1 3 5 7
9 11 13 15
17 19 21 23
虽然在二维数组中a=a[0]=&a[0][0],但在例1中对指针变量赋值的语句只能写成p=a;而不能写成p=a[0]或p=&a[0][0];而在例2中,由于a[0]或&a[0][0]都是一级地址,所以p=a[0]可以代换成p=&a[0][0]。
3.3 设p为指针变量,若其定义语句为 int
(*p)[n][m]则p为三级指针变量,它的初值是三级指针,此时p+1指向下一层,亦即三级指针按层移动。
例3、输出三维数组某一行某一列元素的下标值
main ()
{static int a[2][3][4]
={1,3,5,7,
9,11,13,15,
17,19,21,23,
25,27,29,31,
33,35,37,39,
41,43,45,47};
int i,j,k,m;
int (*p) [3][4];/* 定义一个三维的层指针 */
p=a; /* 把三级地址赋给三级指针变量 */
printf (″a[i][j][k]= ″);
scanf (″%d ″, &m);
for (i=0; i<2; i++)
for (j=0; j<3; j++)
for (k=0; k<4; k++)
if(*(*(*(p+i)+j)+k)==m)
printf (″i=%d, j=%d, k=%d\n ″, i+1,j+1,k+1);
}
运行结果:
a[i][j][k]=45
i=2,j=3,k=3
例1、例2为谭浩强编C程序设计教材中的例题,例3为自编例题。
所有程序在PC486/DX66、Turbo C 2.0环境下调试通过。