多维数组并不是一种全新的类型。它是一种数组类型,其中元素本身就是数组。引用 C99 标准 §6.2.5 ¶20(类型)
数组类型描述了一个连续分配的非空集合
具有特定成员对象类型的对象,称为元素类型。
数组类型的特征在于它们的元素类型和数量
数组中的元素。
int b[2][2] = {{111, 222}, {333, 444}};
上述语句将b 定义为2 元素的数组,其中每个元素的类型为int[2] - 一个2 整数数组。它还使用数组初始值设定项列表初始化数组。在某些情况下,数组会隐式转换为指向其第一个元素的指针。
在printf 调用中,b 衰减为指向其第一个元素的指针。因此,它等价于&b[0] 并且具有int (*)[2] 类型——一个指向2 整数数组的指针。请注意,访问数组边界之外的元素是未定义的行为。因此,for 循环条件i < 100 是错误的。应该是i < 2。现在,让我们试着揭开**(b+i)这个表达式的神秘面纱。
b -------------> pointer to a[0]
b + i ---------> pointer to a[i]
*(b + i) -----> a[i]
*(*(b + i)) ---> *(a[i]) ----> *(&(a[i])[0]) ----> a[i][0]
如前所述,数组的元素本身就是数组。因此,a[i] 是一个数组。同样,数组衰减为指向其第一个元素的指针,即指向&(a[i])[0]。在这个指针上应用间接运算符* 可以得到该地址处的值a[i][0]。
您可以通过指针访问数组的元素,但指针类型必须是指向数组元素类型的指针。
#include <stdio.h>
int main(void) {
int b[2][2] = {{111, 222}, {333, 444}};
int (*p)[2] = b;
// (sizeof b / sizeof b[0]) evaluates to
// the size of the array b
for(int i = 0; i < (sizeof b / sizeof b[0]); i++)
// (sizeof *p / sizeof (*p)[0]) evaluates to
// the size of element of the array which is
// itself an array.
for(int j = 0; j < (sizeof *p / sizeof (*p)[0]); j++)
printf("%d\n", *(*(p + i) + j));
return 0;
}
这里,表达式*(*(p + i) + j)可以解码为
p ---------------> pointer to the first element of b, i.e., &b[0]
(p + i) ----------> pointer to b[i], i.e., &b[i]
*(p + i) ---------> the array element b[i] ---> decays to &(b[i])[0]
*(p + i) + j -----> &(b[i])[j]
*(*(p + i) + j) --> the element b[i][j]
因此,表达式*(*(p + i) + j) 等价于b[i][j]。事实上,C99 标准 §6.5.2.1 ¶2 说 -
下标运算符[]的定义是E1[E2]是
等同于(*((E1)+(E2)))
这意味着我们有以下与上述程序的上下文等效 -
*(*(p + i) + j)
// equivalent to
p[i][j]
// equivalent to
b[i][j]
// equivalent to
*(*(b + i) + j)