指针与二维数组也有三种表示形式。
1. 第一种形式:用指向二维数组元素的指针变量
#include<stdio.h>
int main()
{
int i,*p,n,m;
scanf("%d%d",&n,&m);
int a[n][m];
for(p=a[0];p<a[0]+n*m;p++)
{
scanf("%d",p); // p+i
printf("%d\t",*p); // *(p+i)
}
return 0;
}
因为二维数组a中的n*m个元素在内存中是依序存放的,p=a[0];是把p指向了二维数组的起始地址,也即这n*m个元素的初始地址,然后采用一维数组中的指针变量法来访问数组a中的各个元素。
当然,也可以采用一维数组中的首地址法来访问数组中的各个元素。
#include<stdio.h>
int main()
{
int i,*p,n,m;
scanf("%d%d",&n,&m);
int a[n][m];
for(p=a[0],i=0;i<n*m;i++)
{
scanf("%d",p+i);
printf("%d\t",*(p+i));
}
return 0;
}
通过首地址加偏移量的方法即可访问二维数组a中的各个元素。
有同学会说,老师,能否用指针操作一维数组中的下标法来操作二维数组呢?问题没有那么简单额。
2. 第二种形式:指针数组
对于数组a[2][3]来说,数组a包含了两行a[0]和a[1],而每行又包含了3个元素,因而a[0]和a[1]如同一维数组,因而我们可以定义一个指针数组,用a[0]和a[1]对该指针数组进行初始化。int *p[2]={a[0],a[1]}; p是数组名,数组中有两个元素,两个元素的类型为int*。
`
#include<stdio.h>
int main()
{
int i,j,a[2][3];
int *p[2]={a[0],a[1]};
for(i=0;i<2;i++)
for(j=0;j<3;j++)
{
scanf("%d",p[i]+j); // &p[i][j]
printf("%d\t",*(p[i]+j)); //p[i][j]
}
return 0;
}
这样定义之后,p的地位和数组a的地位相同,因而凡是可以用a[i][j]的地方,均可以用p[i][j]来描述。
比较有意思的是,由于p[i]可以用*(p+i)来描述,因而p[i]+j可以改写为:*(p+i)+j,而*(p[i]+j)则可以描述为:*(*(p+i)+j)。
3. 第三种形式:指向一维数组的指针变量,相当于行指针
先调试下面的代码,看看会有什么情况发生?
#include<stdio.h>
int main()
{
int a[2][3],i,j;
int **p; //改为int *p试试看??
p=a;
for(i=0;i<2;i++)
for(j=0;j<3;j++)
{
scanf("%d",&p[i][j]);
printf("%d\t",p[i][j]);
}
return 0;
}
调试:如果我们定义:int **p;
当系统编译到语句p=a;时会发出警告:
E:\VC6.0完整绿色版\VC6.0完整绿色版\Common\MSDev98\Bin\a.c(8) : warning C4047: ‘=’ : 'int ** ’ differs in levels of indirection from ‘int (*)[3]’
而在你们深爱的codeBlocks中,如果我们定义:int **p;
当系统编译到语句p=a;时会给出警告:
C:\Users\Administrator\Documents\Untitled1.c|6|warning: assignment from incompatible pointer type
我们通常会对警告置之不理,仍坚持输入数据1 2 3 4 5 6,当敲下回车时,会弹出如下图所示的对话框。这个对话框出现就意味着对内存的操作出现了问题。
从VC++6.0的警告中,可知int a[2][3]中,a的类型为int (*a)[3],所以为了和它类型相同,地位相同,需要把指针变量定义为:int (*p)[3]; 如此这般,才可以把p=a;对p的操作才可以如同对a的操作那般。那么,如何来理解int (*a)[3]呢????
我们先来看一个程序:
#include<stdio.h>
int main()
{
int a[2][3]={0};
printf("%x\n",a);
printf("%x\n",a+1);
printf("%x\n",a[1]);
return 0;
}
从运行结果可以看出,a+1指的是a[1]这一行的首地址。由于二维数组名是地址,地址也就意味着是指针,但该指针每加上1就移动了一行,可见a是一个指向包含3个元素的指针,需要定义一个也指向包含3个元素的指针变量,然后把a赋给该指针变量,随后就可以通过该指针变量对a中的元素进行存取。
正确程序如下:
#include<stdio.h>
int main()
{
int a[2][3],i,j;
int (*p)[3];
p=a;
for(i=0;i<2;i++)
for(j=0;j<3;j++)
{
scanf("%d",&p[i][j]);
printf("%d\t",p[i][j]);
}
return 0;
}
综上所述,采用指针对二维数组来进行操作,一共有3中描述形式:
int a[2][3]; int a[2][3];
int *p[2]={a[0],a[1]}; int (*p)[3]=a;
a[i][j]的地址(6种形式是等价的)// i是行,j是列
&a[i][j],a[i]+j,*(a+i)+j
&p[i][j],p[i]+j,*(p+i)+j
a[i][j]的取值(6种形式是等价的):
a[i][j],*(a[i]+j),*(*(a+i)+j)
p[i][j],*(p[i]+j),*(*(p+i)+j)
4. 扩展知识
其实,二维数组中的元素在内存中的存放是按序存放的,它们离首地址的偏移量与它们的行标和下标有关系。分析下面程序:
#include<stdio.h>
int main()
{
int a[2][3]={1,2,3,4,5,6},i,j;
int *p=(int*)a;
for(i=0;i<2;i++)
for(j=0;j<3;j++)
{
printf("%d\t",*(p+i*3+j));
}
return 0;
}
思考:p+i*3+j是什么意思呢?
其实,i*3+j是元素a[i][j]
与数组首地址的偏移量。首地址存入指针变量p中,p+偏移量自然是a[i][j]的地址。