1.定义二维数组
来看下面这个例子:
//例子1.1
int a[10]={0,1,2,3,4,5,6,7,8,9};
这里定义了一个一维数组,并且我们知道数组元素在内存中呈线性排列;
除此之外还可以通过数组序号来访问元素,也可以通过数组名使用指针间接访问元素;
那么对于二维数组来说情况又如何呢,请看下面的定义举例:
//例子1.2
//定义一个二维数组
int b[3][5]={
{1 ,2 ,3 ,4 ,5 },
{6 ,7 ,8 ,9 ,10},
{11,12,13,14,15}
};
显然 b 数组是一个 3 行 5 列的二维数组,共存储了 15 个元素;对于二维数组的访问可以直接使用下标,例如 b[1][3]=9(别忘了数组下标从0开始);可是指针在这里怎么发挥作用。似乎那三行看起来像是三个一维数组,而且每个一维数组有 5 个元素。
不妨我们从这个数组名开始下手;
请注意:虽然二维数组是利用矩阵的方法定义的,但是其在内存中的存贮依旧是线性的。
2.利用指针访问二维数组
在例子1.2中我们定义了一个二维数组 b,下面将考虑如何利用指针来访问数组元素。
前面提到过,尽管二维数组是在逻辑上以矩阵的形式定义的但其在内存中依旧是线性排列的;
编写一个程序试试,就从数组名 b 开始;
//例子2.1
//访问二维数组元素第一次尝试
#include<stdio.h>
int main()
{
int b[3][5] = {
{ 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10},
{ 11,12,13,14,15}
};
printf("b =%p\n",b);
printf("*b =%p\n",*b);
printf("*(b+1)=%p\n",*(b+1));
return 0;
}
结果如下:
b与*b的地址值相同,很奇怪,按照原来的理解b是数组名,是一个地址。那么*b至少也应该是个数组中的值,*b怎么还会是一个指针呢?
为了弄清楚刚才的问题,让我们把目光放在 *(b+1) 的地址上,按照16进制的计算法(00CFF98C-00CFF978)结果等于十进制20,熟悉吗?
20 不就是 5*sizeof(int)吗,而二维数组b[3][5]正好是五列的。我们对 b 加 1, 结果地址直接加了五个字节,这给我们些许启发。
请看下面的图例:
正如图例所示,二维数组的第二个参数看起来是对第一个参数形成空间的再分割。
至于 *b 为什么还是指针显然一清二楚了,现在每一行被理解为一个一维数组。对二维数组名b执行一次间接访问(即*b)正是由行的访问变为列的访问,可以理解为此时 *b 就是第一个行数组的数组名(注意:二维数组的第一行序号为0,*b 就是 *(b+0)),前面介绍过数组名依旧是地址;所以**b 就是第一个元素的值了。
我们来验证一下刚刚的说法:
//例子2.2
//访问二维数组元素的第二次尝试
#include<stdio.h>
int main()
{
int b[3][5] = {
{ 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9, 10 },
{ 11, 12, 13, 14, 15 }
};
printf("第一个元素 %d\n", **b);
printf("第一行第二个元素 %d\n",*(*b+1));
printf("第二行第四个元素 %d\n", *(*(b + 1) + 3));
return 0;
}
结果如下:
结果正是我们想要的,不过表达式看起来有些复杂,我把他们单独列出来解释;
**b
正如前面所说,他的值就是数组第一个元素的值如果把他写完整了就是*(*(b+0)+0);其中*(b+0)就是第一行的第一个元素的地址,*(b+0)+0也是第一行第一个元素的地址,毕竟加0了吗没改变什么,*(*(b+0)+0)这个理所当然就是值了即第一行第一个元素的值。
*(*b+1)
很显然,如果你明白了上面的叙述他就是*(*(b+0)+1),即第一行第二个元素的值。
*(*(b+1)+3)
我相信你知道答案了,没错他就是第二行第四个元素的值。
👍!
[参考书目]《C和指针》Kenneth A.Reek 著.人民邮电出版社.