引言
在学习了一维数组指针之后,我们可能会对多维数组指针和指向分数组的指针产生疑惑,下面我将以二维数组为例,简单地为大家解释一下该内容。
正文
首先我们定义一个二维数组a[3][4]
int a[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
二维数组本质上也是一维数组,在物理存储上它仍是线性排列,只不过在逻辑上是具有空间概念的,我们来运行下列代码来验证我们的想法。
int a[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int i,j;
printf("sizeof(int)=%zu\n",sizeof(int));
for(i=0;i<3;i++)
for(j=0;j<4;j++)
printf("a[%d][%d]的地址是:%p\n",i,j,&a[i][j]);
运行结果:
sizeof(int)=4
a[0][0]的地址是:000000000062FDE0
a[0][1]的地址是:000000000062FDE4
a[0][2]的地址是:000000000062FDE8
a[0][3]的地址是:000000000062FDEC
a[1][0]的地址是:000000000062FDF0
a[1][1]的地址是:000000000062FDF4
a[1][2]的地址是:000000000062FDF8
a[1][3]的地址是:000000000062FDFC
a[2][0]的地址是:000000000062FE00
a[2][1]的地址是:000000000062FE04
a[2][2]的地址是:000000000062FE08
a[2][3]的地址是:000000000062FE0C
也就是:
a[0][0] | a[0][1] | a[0][2] | a[0][3] | a[1][0] | a[1][1] | a[1][2] | a[1][3] | a[2][0] | a[2][1] | a[2][2] | a[2][3] |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
了解二维数组的物理存储后我们了解一下对应的指针,技巧和方法如下:
Key
1. 数组名是指向0号分量的指针常量,基类型大小为0号分量的大小
例如:
a是指向0号分数组的指针常量,它的基类型大小是整个0号分数组的长度,即sizeof(int)*4。
a[0]是指向0号分数组中0号元素的指针常量,它的基类型大小是sizeof(int)
需要注意的是,a[0[0]不是一个数组名,它就是一个数组元素,也就是说,a[0][0]就是1
2. 简单公式替换:解引用运算符[ ]相当于*( + )
例如:
a[0] ~ *(a+0) ~ *a
a[0][0] ~ *(*(a+0)+0)
3.其他理解方式
行指针a纵向移动i到达到a+i,然后解引用得到*(a+i),即a[i]——列指针,然后列指针a[i]横向移动j步到达(a[i]+j)),最后解引用得到*(a[i]+j),即a[i][j];
对应的指针运算
利用该条规则,我们也能够很好的解释指针运算,例如:
a+1 ~ a[1]
a[0]+1 ~ a[0][1]
那么&a+1是什么呢?
答案是:它是指向整个数组a后面那个位置,基类型大小为整个数组大小的指针
可以这么理解:*运算符是解引用,它往里解一层,而&运算符是往外跳一层。
据此:
&a[0] ~ a
&a[0][0] ~ a[0]
上面提到过,a是指向0号分数组的指针常量,往外跳一层就是指向整个数组的指针,我们可以认为有一个实际上不存在的指向整个数组a,基类型为整个数组a大小的指针,而&(往外跳一层)a就是它。
最后我们用代码来验证上述内容:
int a[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int i,j;
printf("sizeof(int)=%zu\n",sizeof(int));
printf("a:%p sizeof(*a)= %d\n",a,sizeof(*a));
printf("a[0]:%p sizeof(*a[0])= %d\n",a,sizeof(*a[0]));
printf("a[0][0]:%d\n",a[0][0]);
printf("a[0]:%p ~ *(a+0):%p ~ *a:%p\n",a[0],*(a+0),*a);
printf("a[0][0]: %d ~ *(*(a+0)+0): %d\n",a[0][0],*(*(a+0)+0));
printf("a+1: %p ~ a[1]: %p\n",a+1,a[1]);
printf("a[0]+1: %d ~ a[0][1]: %d\n",a[0]+1,a[0][1]);
printf("&a+1: %p\n",&a+1);
printf("&a[0]: %p ~ a: %p\n",&a[0],a);
printf("&a[0][0]: %p ~ a[0]: %p\n",&a[0][0],a[0]);
运行结果:
sizeof(int)=4
a:000000000062FDF0 sizeof(*a)= 16
a[0]:000000000062FDF0 sizeof(*a[0])= 4
a[0][0]:1
a[0]:000000000062FDF0 ~ *(a+0):000000000062FDF0 ~ *a:000000000062FDF0
a[0][0]: 1 ~ *(*(a+0)+0): 1
a+1: 000000000062FE00 ~ a[1]: 000000000062FE00
a[0]+1: 6487540 ~ a[0][1]: 2
&a+1: 000000000062FE20
&a[0]: 000000000062FDF0 ~ a: 000000000062FDF0
&a[0][0]: 000000000062FDF0 ~ a[0]: 000000000062FDF0