C 中数组速成课程的时间到了。
首先,让我们修复数组的初始化器:
int a[3][4] = {
{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9, 10, 11, 12}
};
这定义了 int 的 4 元素数组的 3 元素数组。 表达式 a 的类型是“int 的 4 元素数组的 3 元素数组”。
现在是引起头痛的部分。除非它是 sizeof 或一元 & 运算符的操作数,或者如果它是用于在声明中初始化另一个数组的字符串文字,则数组类型的表达式将其类型隐式转换(“衰减”)为指针类型。
如果表达式a在代码中单独出现(例如在printf("%p", a);这样的语句中,其类型从“int的4元素数组的3元素数组”转换为“指针指向int的四元数组”,或者int (*)[4]。同理,如果代码中出现a[i]这个表达式,其类型由“int的四元数组”(int [4])转换为“指向int" (int *) 的指针。如果a 或a[i] 是sizeof 或& 的操作数,则不会发生转换。
类似地,数组下标是通过指针算术完成的:表达式a[i] 被解释为好像写成*(a+i)。您从数组的底部偏移i 元素并取消引用结果。因此,a[0] 与*(a + 0) 相同,与*a 相同。 a[i][j] 与写 *(*(a + i) + j) 相同。
下表总结了以上所有内容:
表达式类型衰减为结果值
---------- ---- --------- -----
a int [3][4] int (*)[4] 数组第一个元素的地址
&a int (*)[3][4] n/a 同上,但类型不同
*a int [4] int * 同上,但类型不同
a[0] int [4] int * 同上
*(a+0) int [4] int * 同上
a[i] int [4] int * 第 i 个子数组的第一个元素的地址
*(a+i) int [4] int * 同上
&a[i] int (*)[4] n/a 同上,但类型不同
*a[i] int n/a 第 i 个子数组的第 0 个元素的值
a[i][j] int 第 i 个子数组的第 j 个元素的值
*(a[i]+j) int 同上
*(*(a+i)+j) int 同上
希望这应该为您提供确定输出应该是什么所需的一切。但是,printf 语句应该写成
printf("%p %d %d\n", (void *) a[0]+1, *(a[0]+1), *(*(a+0)+1));