二维数组名的含义
弄清这个问题前,首先要搞明白一维数组的数组名代表的含义
#include<stdio.h>
int main(void){
int a[3]={1,2,3};
int *p;
p=a;
printf(" a=%d\n",a);
printf(" &a[0]=%d\n",&a[0]);
printf(" &a=%d\n",&a);
printf(" *a=%d\n",*a);
printf(" *p=%d\n",*p);
}
运行结果
我们知道数组是在内存里占据一片连续的内存空间,由此可以看到 数组名a的值为数组 a的第一个元素的地址,且数组名a自身的地址也和a指向的地址相同,即 a=&a=&a[0]
由此可以发现,数组名代表一个指针变量,它指向数组的第一个元素也即是自己在内存中的地址(自己指向自己)。
**在此基础上,我们再来看二维数组**
#include<stdio.h>
int main(){
int a[3][5];
int (*p)[5];
p=a;
printf(" a=%d\n",a);
printf(" &a[0][0]=%d\n",&a[0][0]);
printf(" &a=%d\n",&a);
printf(" *a=%d\n",*a);
printf(" *p=%d\n",*p);
}
输出结果:
由运行结果可知, a=&a=&a[0 ][0],这点和一维数组一样,二维数组名a存储的地址(数组第一个元素的地址),也是其本身在内存中的地址。
但和一维数组不同的是,二维数组名是一个二重指针,即指针的指针。
故在本例中,二维数组a[3][5]的数组名a代表为指向含有5个元素的数组的指针,所以 (p)[5]才能被a赋值,即p=a。(注意p[5]和(*p)[5]这两种表示方法的区别)。
又因为编译器从数组a的基本地址&a[0][0]开始,按行为所有元素分配连续的储存空间。也就说,第二行的第一个元素紧邻第一行的最后一个元素(这就是为什么在声明二维数组时,必须指定每行大小的原因,这样,编译器就可以确定正确的存储映射了)。故在二维数组中,可以用如下指针表达式来表示元素:
*(*(a+i)+j)或 *(*(p+i)+j)
通过下面的图片可以梳理一下逻辑关系: