在下面的代码中,我得到了地址相同的前两个值(称之为x(。我已经在 ubuntu v18.04.4 LTS 的 gcc 编译器上运行了它。
int a[2][2] = {0};
printf("%p %p %dn", a, *a, **a);
这意味着:
- a 包含地址 x。
- a 指向位置 x(因为它是存储 x 的指针(。
- 这意味着 *a 存储在位置 X 中,它还包含值 X(如上述代码的输出所示(。
- 现在,在取消引用 *a 即 **a 时,我得到的输出为 0,这意味着 *a(其值为 x(指向存储 0 的某个位置。
现在,从 1. 开始,*a 的地址是 x(因为 a 指向 *a 并存储 x(,数字 0 的地址也是 x(因为 *a 指向 a[0][0],即 0,*a 存储 x(。 所以我的问题是位置 x 到底存储了什么? 还是我在得出结论时弄错了什么?
您断言a
包含地址似乎表明您认为a
是一个指针。 它不是一个指针,而是一个数组。 它们是相关的,但它们不是一回事。
在大多数情况下,数组衰减到指向其第一个成员的指针。 在这种情况下,这意味着在表达式中a
与&a[0]
相同,*a
与a[0]
相同,&a[0][0]
相同。 这也意味着数组的地址与其第一个成员的地址相同,这是您在打印a
和*a
时看到的。
用图表可能更好地说明这一点:
----- ------- ---- ---
0x100 | 0 | a[0][0] a[0] a
----- -------
0x104 | 0 | a[0][1]
----- ------- ----
0x108 | 0 | a[1][0] a[1]
----- -------
0x10c | 0 | a[1][1]
----- ------- ---- ---
从这里,您可以看到a
从地址 0x100 开始(在您的示例中x
(,总共包含 4 个int
值。 还要注意子数组a[0]
与a
具有相同的地址,初始int
元素a[0][0]
也是如此。
概括这一点,数组的地址和它的第一个元素的地址,即使类型不同,也是相同的。 所以:
a
是一个地址为 0x100 的数组。 它包含类型的元素int[2]
.a[0]
是一个地址为 0x100 的数组。 它包含类型的元素int
.a[0][0]
是一个地址为0x100的int
。
我不明白什么是神奇的'x'
.;)
您声明了一个二维数组
int a[2][2] = {0};
表达式中使用的数组指示符(极少数例外,例如在 sizeof 运算符中使用它们(隐式转换为指向其第一个元素的指针。
所以表达式a
在此调用中使用
printf("%p %p %dn", a, *a, **a);
转换为类型int ( * )[2]
数组第一个元素的指针。也就是说,它是数组占用的内存范围的地址。
使用间接寻址运算符 * 表达式*a
生成原始数组a
的类型int[2]
的第一个元素。
同样,在 printf 调用中*a
的这个数组指示符被隐式转换为类型int *
指向其第一个类型为int
的元素的指针。此指针将包含原始数组占用的内存扩展数据的相同地址。
在此表达式**a
中应用了两个间接寻址运算符。第一个间接寻址运算符生成二维数组的第一个元素,即它生成类型为int[2]
的数组。此数组一次用作第二个间接寻址运算符的操作数,将隐式转换为指向其类型为int *
的第一个元素的指针。第二个间接寻址运算符生成指针指向的对象,该指针是原始数组的对象,a[0][0]
由上面所示的 printf 调用输出。由于此元素由 0 显式初始化,因此 0 作为元素的值输出。
为了更清楚起见,第一个间接寻址运算符*a
等效于使用下标运算符 a[0]。而应用于此表达式*a[0]
的第二个间接寻址运算符等效于表达式a[0][0]
。