2024年2月4日发(作者:)
二维数组的指针访问
--王炳华
指向二维数组的指针及用指针访问二维数组,是学习指针的最大难点。如果真正弄懂了这个问题,就可以说你学会了使用指针。
一、 二维数组的指针
指针就是地址,二维数组的指针就是二维数组在内存中的存储地址。相对于一维数组的地址而言,二维数组的地址稍微复杂一点。
二维数组的地址与一维数组的地址的相同点在于:①、它们的每一个元素都有一个存储地址(称为元素地址);②、它们都是将全部元素在内存中按顺序占用一段连续的存储空间;即对于一维数组,下标为1的元素的存储单元紧接在下标为0的元素的存储单元之后,下标为2的元素的存储单元紧接在下标为1的元素的存储单元之后......直到最后一个元素。对于二维数组,下标为0的行的各个元素按顺序存储完之后,下标为1的行的元素紧接其后按顺序存储......直到最后一行的最后一个元素。
二维数组的地址与一维数组的地址的不同点是:它除了有元素地址外,还有标识各行起始位置的行首地址(称为行的首地址)。
行的首地址和行的首元素的地址具有相同的地址值,但是它们是两种不同的地址:若有定义int a[5][5];则a[0][0]是a数组首行首列元素(代表该元素的值)。而&a[0][0]是首行首元素的地址。&&a[0][0]则是首行的首地址。从这个意义上讲,可以说行的首地址是一种二重地址。
二、 行的首地址、行的首元素地址和行的首列元素的值的关系
可以把某行的首地址、某行首列元素的地址、某行首列元素(代表它的值),看成是由高到低的三个层次。
某行首列元素作一次&运算得到该行首列元素的地址,某行首列元素的地址再作一次&运算得到该行的首地址。
从这个意义上讲,可以说元素的地址是一重地址,而行的首地址是二重地址。
某行的首地址作一次* 或[ ]运算得到该行的首元素的地址,某行的首元素的地址作一次* 或[ ]运算得到该行的首元素的值。
三、 运算符 *、&、[ ] 之间的关系
1. [ ]运算符
[ ]是下标运算符,只适用于数组和指向数组的指针变量。其优先级与( )同级,高于 * 和& 。结合方向是左结合性(自左至右)。
2. 三者的关系
* 与 & 互为逆运算
* 与 [ ] 等效
[ ] 与 & 互为逆运算
3. 作用
前面已经提到:可以把某行的首地址、某行首列元素的地址、某行首列元素(代表它的值),看成是由高到低的三个层次。
* 和 [ ] 都是将运算对象从高层向低 层转化。
& 是将运算对象从低层向高层转化。
如:行的首地址作一次* 或[ ] 运算得到该行的首元素的地址。
元素的地址作一次* 或[ ] 运算得到该元素的值。
而元素(代表它的值)作一次&运算得到该元素的地址。
某行的首元素的地址作一次&运算得到该行的首地址。一
四、 数组名是地址常量
若有定义int b[5];一维数组名b是什么?
b与b+0 是等价的;由于* 与 & 互为逆运算,所以b+0 与&*(b+0) 等价;由于* 与
[ ] 等效,所以*(b+0) 与b[0] 等价,&*(b+0)就与&b[0] 等价。可见一维数组名b与&b[0] 等价。&b[0] 是一维b数组首元素的地址,可见一维数组名b是一维b数组首元素的地址,也称为一维数组的基地址。
若有定义int a[5][5];二维数组名a是什么?
a与a+0 是等价的;由于* 与 & 互为逆运算,所以a+0 与&*(a+0) 等价;由于* 与
[ ] 等效,所以*(a+0) 与a[0] 等价,&*(a+0)就与&a[0] 等价。
&a[0]与&a[0]+0是等价的。由于* 与 & 互为逆运算,所以
&a[0]+0与&*(&a[0]+0) 等价;由于* 与 [ ] 等效,所以*(&a[0]+0) 与&a[0][0] 等价,&*(&a[0]+0) 就与&&a[0][0] 等价,可见二维数组名a与&&a[0][0] 等价。
&&a[0][0] 是二维数组a的首行的首地址,可见二维数组名a是二维数组a的首行的首地址。
必须指出:数组名是一种地址常量,不能作++、 --、+=、- = 、= 等运算;可以作+运算,不能作 - 运算;可以作* 运算,一般不作&运算;可以与指向本数组的同类型的指针作关系运算,一般不作逻辑运算。
五、 行的首地址、元素的地址及元素的值的常见形式
1. 行的首地址的表示形式
若有一个m行n列的二维数组a [m][n] 。数组名a是它的首行的首地址,也即是它0行的首地址。
从刚才的推导出:二维数组a 的首行的首地址有a 、 a+0 、 &a[0] 、&a[0]+0以及&&a[0][0]等五种形式。
由于a+0是0行的首地址。a+1就是1行的首地址,a+i就是i行的首地址。
由于* 与 & 互为逆运算,a+i与&*(a+i) 等价,由于* 与 [ ] 等效,所以*(a+i) 与a[i] 等价,&*(a+i)就与&a[i] 等价。
&a[i]与&a[i]+0是等价的。由于* 与 & 互为逆运算,所以&a[i]+0与&*(&a[i]+0) 等价;由于* 与 [ ] 等效,所以*(&a[i]+0) 与&a[i][0] 等价,&*(&a[i]+0) 就与&&a[i][0] 等价。
这样i行的首地址有a+i 、 &a[i] 、&a[i]+0和&&a[i][0] 四种形式。
2. 元素的地址
前面已经说过:行的首地址作一次* 或[ ] 运算得到该行首列元素的地址。
对首行的首地址的五种形式a 、 a+0 、 &a[0] 、&a[0]+0以及&&a[0][0]作 一次* 运算为:* a 、 * (a+0)、* &a[0] 、*&a[0]+0、*&&a[0][0] ,其中 * &a[0]即a[0] ,*&a[0]+0即a[0]+0,*&&a[0][0]即&a[0][0],都是首行首列元素的地址。
这样二维数组a 的首行首列元素的地址就有:
* a 、 * (a+0)、* (a+0)+0、a[0] 、a[0]+0 、&a[0][0]、等六种形式。
相应地把代表行号的0换成i,可得到二维数组a 的i行首列元素的地址:
* (a+i)、* (a+i)+0、a[i] 、 a[i]+0、&a[i][0]等五种形式。
而把代表列号的0换成j,可得到 i行j列的元素的地址:
* (a+i)+j、a[i]+j 、&a[i][j]等三种形式。
3. 元素的值
由于元素的地址作一次* 运算得到元素的值。对上述i行j列元素的地址的三种形式
&a[i][j]、* (a+i)+j、a[i]+j作一次* 运算即:
*&a[i][j]、* ( * (a+i)+j )、* (a[i]+j ) 都是i行j列元素的值。
其中*&a[i][j] 就是a[i][j] 。
这样二维数组a 的i行j列元素的值有:
a[i][j] 、* ( * (a+i)+j )、* (a[i]+j ) 等三种形式。
六、 二维数组的指针访问方法:
二维数组的指针访问方法有两种。
一种方法是用一个指向元素的指针*jp,先让它指向二维数组的首行的首列元素,在循环中连续用jp++,该指针将先逐一访问0行上的各个元素,再访问1行上的各个元素,直到最后一行最后一个元素。
另一种方法是用两种不同的指针变量:
一种指针变量是指向行的指针,称为行指针。使它获得行的首地址,它只能指向各行的行首,而不能指向某个元素。这种指针做一次++运算,是从上一行的行首移动到下一行的行首。
另二种指针变量是指向元素的指针,称为元素指针。使它获得元素的地址,它是指向元素的。这种指针做一次++运算,是从前一个元素移动到下一个元素。
访问二维数组时,在外层循环中用行指针,先使它指向首行的行首,用++运算可以逐个访问各行的行首。内层循环中用元素指针,使它指向行指针所指的行的首列元素,用++运算可以逐个访问该行的各个元素。
值得注意的是:
行指针变量只能存放行的首地址,不能存放元素的地址。
元素指针变量只能存放元素的地址,不能存放行的首地址。
七、 指向元素的指针变量
1、 指向元素的指针变量的定义
与指向一维数组的指针变量的定义形式完全相同,即:
类型说明 * 指针变量名;
如:int * jp ; 就定义了一个可以指向整型数组的元素的指针变量。
2、 指向元素的指针变量的赋值
指向元素的指针变量只能将元素的地址赋给它,不能将行的首地址赋给它。
3、 指向元素的指针变量的引用
指向元素的指针变量无论是用来访问一维数组还是用来访问二维数组,它每作一次+1运算都是从前一个元素移动到后一个元素。
4、 用指向元素的指针变量访问二维数组
若将二维数组的首行首列元素的地址赋给指向元素的指针变量 * jp ,连续作
jp++ , 指针将从首行首列移到首行1列直至首行最后一列,接着移到1行首列......到1行最后一列,接着移到2行首列......最后移动到最后一行最后一列。访问完二维数组的每一个元素。
5、 例题:用指向元素的指针变量生成一个由自然数1~25组成的5×5方阵,并输出。程序如下:
#include
void main()
{int a[5][5],*jp=*a, i, j;
printf("n");
for(i=1;jp<*a+25;jp++ )
{ *jp=i++;
printf("%5d",*jp);
if((i-1)%5==0) printf("n");
}
for(i=0;i<5;i++) /* 以下四行组织的输出是为了证实这些数是否存入了a数组*/
{for(j=0;j<5;j++)
printf("%6d",a[i][j]);
printf("n"); }
}
八、 指向有m个元素的一维数组的指针变量
1、 指向有m个元素的一维数组的指针变量的定义
形式:类型说明 (* 指针变量名)[正整型常量];
若想定义一个指向有m个元素的一维数组的指针变量 ip ,用它来指向有5个元素的一维整型数组,则是:
int ( * ip ) [ 5 ] ;
注意:( *ip )的括号()不能少,如果不写()就成了 * ip [5] , 因为 [ ] 比* 的运算级别高,[5]将先与ip 结合成为 ip[5] , 则ip 就是一个有5个元素的一维数组的数组名,再与* 结合成 * ip[5] 就是有5个指针元素的指针数组。后面可以看到这种形式正是指针数组定义的形式。
2、 指向有m个元素的一维数组的指针变量的赋值
指向有m个元素的一维数组的指针变量是行指针变量,只能将行的首地址赋给它,不能将行的首列元素的地址赋给它。
3、 指向有若m元素的一维数组的指针变量的引用
指向有m个元素的一维数组的指针变量,每作一次 + 1 运算是从上一行行首移动到下一行行首,而不是从前一列移动到后一列。
4、 用指向有m个元素的一维数组的指针变量访问二维数组
用指向有若干个元素的一维数组的指针变量访问二维数组的各行,再用一个指向元素的指针访问行上的每个元素,实现对二维数组各元素的访问。
5、 例题:生成一个5×5的整型方阵。该方阵中每个元素的值都的一个二位数,其十位上的数字是它所在的行号+1,个位上的数字是它所在的列号+1,并输出。
程序如下:
#include
void main()
{ int a[5][5],(*ip)[5],*jp,i,j;
printf("n");
for(i=0,ip=a;ip<=a+4;ip++,i++)
{ for(jp=*ip,j=0;jp<=*ip+4;jp++,j++)
{ *jp=(i+1)*10+j+1;
printf("%5d",*jp);
}
printf("n");
}
for(i=0;i<5;i++) /* 以下的输出是为了证实这些数是否存入了a数组*/
{ for(j=0;j<5;j++)
printf("%6d",a[i][j]);
printf("n");
}
}
九、 指针数组
指针数组是存储指针变量的数组。因为指针数组的各个元素可以用来指向二维数组的行,所以先介绍指针数组。
1、 指针数组的定义
形式:类型说明 * 指针数组名[正整型常量]
如:int * p[5] ;
就定义了一个有五个指针元素的指针数组,每个元素都是指向整型数组元素的元素指针变量。
2、 指针数组的初始化
指针数组的每个元素都是一个指针变量,每个指针都可以指向一个一维数组。因为二维数组的每一行都是一个一维数组,因此可以把二维数组各行首列元素的地址作为指针数组元素的值。
如:static int a[5][5] , *p[5]={* (a+0), * (a+1), * (a+2), * (a+3), * (a+4)};
或者:static int a[5][5] , *p[5]={a[0], a[1], a[2], a[3], a[4]};
注意,这里一定要把a数组定义成static 类型。因为初始化的{ }中的值应该是大小不变的常量,而这里都不是常量而是表达式,若a数组不定义成static 类型,则是在程序执行时动态分配存储单元,这些表达式的值就会发生变化。
当然也可以不进行初始化,在程序执行中赋值。
3、 用指针数组的元素指向二维数组的各行访问二维数组
例题:将3个人每人4门课的考试成绩读入一个二维数组,并统计出各个分数段的成绩的门数(60分以下为一段,其余每10分为一段)。程序如下:
#include
void main()
{static float g[3][4],*ip[3]={*(g+0),*(g+1),*(g+2)};
static int m[6],*ms=m,n,i,j;
printf("n");
for(i=0;i<=2;i++)
{for(;ip[i]<=*(g+i)+3;ip[i]++)
{scanf("%f",ip[i]);
n=*ip[i]<60?0:*ip[i]/10-5;
ms[n]++;
}
printf("n");
}
for(i=0;i<3;i++) /* 以下用下标法输出是为了证实成绩是否存入了g数组*/
{for(j=0;j<4;j++)
printf("%6.1f",g[i][j]);
printf("n"); }
printf("0<60 60~<70 70~<80 80~<90 90~<100 100n");
for(;ms<=m+5;ms++)
printf(" %-7d",*ms);
printf("n");
}
十、 指向指针的指针 二重指针
前面介绍了指针数组,指针数组名是指针数组的首元素的地址。如果定义一个指针变量把这个地址量存储起来,这个变量就是指向指针的指针变量,称为二重指针。
1、 二重指针的定义
二重指针定义的形式:
类型说明 * * 指针变量名
如:float **pp 就定义了一个可以指向某个指向实型数据的指针数组的二重指针。若将一个指针数组名赋给它,它就是这个指针数组的元素指针,对它作++运算可以逐个访问指针数组的每个指针元素。
2、 二重指针的初始化
若想用二重指针访问二维数组的各行,应先定义一个静态存储的二维数组。再定义一个指针数组并初始化,使指针数组的各指针元素指向二维数组的各行的行首元素。最后定义一个二重指针,初始化时把指针数组名赋值给它。当然也可以先不进行初始化,在使用时再赋值。下面看例题。
3、 用二重指针访问二维数组
例题:生成如下所示的对称矩阵,并输出。
0 1 2 3 4
1 0 1 2 3
2 1 0 1 2
3 2 1 0 1
4 3 2 1 0
程序如下:
#include
void main()
{static int a[5][5],*p[5]={a[0],a[1],a[2],a[3],a[4]},**ipp=p,*jp;
int i,j;
printf("n");
for(i=0;ipp
{for(j=0,jp=*ipp;j<5;j++,jp++)
{ *jp=i>=j?i-j:j-i;
printf("%5d",*jp);
}
printf("n");
}
for(i=0;i<5;i++) /*以下是将矩阵再输出一次,看是否装入了a数组*/
{for(j=0;j<5;j++)
printf("%6d",a[i][j]);
printf("n"); }
}
第 1 页 共 4页
本文发布于:2024-02-04 04:34:40,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170699248052144.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |