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