所以有一段时间我对数组名称和指针感到困惑。
我们宣布int a[10];
在路上的某个地方也有a
和&a
。
所以我得到了语法的工作原理。 a
是数组名称。当它不被用作sizeof
&
等的操作数时,它将被转换或“衰减”,因此它返回一个指向整数的指针,该整数保存数组的第一个元素的地址。
如果数组名称用作sizeof
或&
的操作数,则其类型为int (*)[10]
。所以我猜这种类型是不同的,因为“衰变”不会发生。
但我仍然不明白&a
是如何工作的。我的理解是它给了我whatever it was before the "decay" happened
的地址..所以在指针发生的“衰变”之前,那么它是什么以及编译器如何使用“原始”来评估&a
?
相比之下,如果我们声明int *p;
,后来在代码中的某处有&p
和p
......
在这种情况下,指向整数p
的指针被赋予一个单独的指针单元及其地址,该地址的值将是我们分配给它的任何地址(或该地址预分配的垃圾值)。
当a
被声明为int a[10]
时,它不会在内存中被分配一个单独的指针单元格。我听说它在寄存器%ebp
上有一个偏移量。然后编译器在评估&a
时会发生什么?没有发生指向整数指针的“衰减”,首先没有单独的“指针”。然后编译器识别a
是什么以及当它看到一元&
运算符使用数组名称作为操作数时它做了什么?
鉴于:
int a[10];
对象a
是int[10]
类型。在大多数但不是所有的上下文中,表达式a
“衰减”到指针表达式;表达式产生类型为int*
的值,相当于&a[0]
。
但我仍然不明白
&a
是如何工作的。我的理解是它给了我在“衰变”发生之前的任何地址。所以在指针发生“衰变”之前,那么它是什么以及编译器如何使用“原始”来评估&a
?
这不太正确。在&a
,衰变根本不会发生。 a
的类型为“10个int
阵列”(int[10]
),因此&a
的类型是“指向10 int
阵列的指针”(int(*)[10]
)。
这没什么特别的。对于foo
类型的任何名称some_type
,表达式&foo
的类型为“指向some_type
的指针”。 (令人困惑的是,这是一种罕见的情况,其中数组名称的行为并不奇怪。)
最好将“数组”和“指针”这两个词视为形容词而不是名词。因此,我们可以有一个数组对象,一个数组表达式,一个数组类型等等 - 但只是“数组”是不明确的。
这个:
int a[10];
定义一个名为a
的数组对象(并分配4 * sizeof (int)
字节来保存它)。没有创建指针对象。您可以通过获取对象的地址或对象的任何元素来创建指针值。这与任何其他类型的对象没有什么不同。定义some_type
类型的对象不会创建some_type*
类型的对象,但您可以通过计算对象的地址来创建类型为some_type*
的值。
那么当编译器看到一元&运算符使用数组名作为操作数时,编译器识别as和它做了什么?
编译器将a
标识为10个元素的整数数组,当它看到&
运算符时,它返回该数组的地址。
就像它会将int i = 3;
视为整数,并将&i
视为该整数的地址。
关于获取数组的地址:数组本身就是一个对象,所以它既有大小又有地址(尽管它的地址很少有用)。
将数组转换为指向其第一个元素的指针是强制类型的一种形式。只有在备选方案是编译错误时才会发生。
例如,您无法将数组与指针进行比较,因此将数组(隐式)强制(强制转换)为int*
(到其第一个元素),然后比较指针类型。在C中,您可以比较任何指针类型。 C只是不关心(虽然它可能会发出警告)。
正如你所说的那样,就类型而言,这实际上是将int*
与int(*)[10]
进行比较。这些必须具有相同的地址(无论键入什么),因为数组直接保存其数据。因此,数组的地址始终是其第一个元素的地址。
但是,获取数组的大小并不是一个错误,所以sizeof(a)
得到整个数组的大小,因为不需要强制使这个合法。所以这和sizeof(int[10])
一样。
如你所说,你的另一个案例sizeof(&a)
真的是sizeof(int(*)[10])
。