为什么arr和&arr一样?

我已经编程了很多年的c / c ++,但今天的偶然发现让我有点好奇……为什么两个输出在下面的代码中产生相同的结果? ( arr当然是arr[0]的地址,即指向arr[0]的指针。我本来期望&arr是该指针的地址,但它与arr具有相同的值)

  int arr[3]; cout << arr << endl; cout << &arr << endl; 

备注:这个问题已经结束,但现在又被打开了。 (谢谢 ?)

我知道&arr[0]arr评估的数字相同,但这不是我的问题! 问题是为什么&arrarr评估相同的数字。 如果arr是文字(不存储任何软件),那么编译器应该抱怨并说arr不是左值。 如果arr的地址存储在某处,那么&arr应该给我该位置的地址。 (但这种情况并非如此)

如果我写

const int * arr2 = arr;

那么对于任何整数iarr2[i]==arr[i] ,但是&arr2 != arr

他们不一样。 他们只是在同一个记忆位置。 例如,您可以编写arr+2来获取arr[2]的地址,而不是(&arr)+2来执行相同的操作。

另外, sizeof arrsizeof &arr是不同的。

 #include  struct foo { int x; int y; }; int main() { foo f; void* a = &f.x; void* b = &f; assert(a == b); } 

出于同样的原因,上面的两个地址ab 是相同的 。 对象的地址与其第一个成员的地址相同(但它们的类型不同)。

  arr _______^_______ / \ | [0] [1] [2] | --------------------+-----+-----+-----+-------------------------- some memory | | | | more memory --------------------+-----+-----+-----+-------------------------- ^ | the pointers point here 

正如您在此图中所看到的,数组的第一个元素与数组本身位于同一地址。

两者具有相同的值但不同的类型。

当它本身使用时(不是&sizeof的操作数), arr求值为指向int的指针,该指针保存数组中第一个int的地址。 &arr计算指向三个int的数组的指针,保存数组的地址。 由于数组中的第一个int位于数组的最开头,因此这些地址必须相等。

如果你对结果进行一些数学运算,两者之间的差异就会变得明显:

arr+1将等于arr + sizeof(int)

((&arr) + 1)将等于arr + sizeof(arr) == arr + sizeof(int) * 3

编辑:关于如何/为什么会发生这种情况,答案很简单:因为标准是这样说的。 特别是,它说(§6.3.2.1/ 3):

除非它是sizeof运算符或一元&运算符的操作数,或者是用于初始化数组的字符串文字,否则将类型为”array of type”的表达式转换为类型为”指针的表达式type”指向数组对象的初始元素,而不是左值。

[注意:这个特别引用来自C99标准,但我相信在C和C ++标准的所有版本中都有相同的语言]。

在第一种情况下( arr本身), arr不被用作sizeof,unary等的操作数,因此它被转换(不提升)到类型“指向类型的指针”(在这种情况下,“指针”)到int“)。

在第二种情况下( &arr ),名称显然被用作一元&运算符的操作数 – 因此不会发生转换。

地址相同但两个表达式都不同。 它们只是从相同的内存位置开始。 两种表达的类型都不同。

arr的值的类型为int * ,而&arr的值的类型为int (*)[3]

&是地址运算符,对象的地址是指向该对象的指针。 指向int [3]类型的对象的指针的类型为int (*)[3]

他们不一样。

更严格的解释:

arrint [3]类型的左值 int [3] 。 尝试在某些表达式(如cout << arr将导致lvalue-to-rvalue转换,因为没有数组类型的rvalues,它将转换为int *类型的rvalue并且值等于&arr[0] 。 这是你可以显示的。

&arrint (*)[3]类型的右值 ,指向数组对象本身。 这里没有魔法:-)这个指针指向与&arr[0]相同的地址,因为数组对象及其第一个成员在内存中的完全相同的位置开始。 这就是打印时得到相同结果的原因。


确认它们不同的一种简单方法是比较*(arr)*(&arr) :第一个是int类型的左值,第二个是int[3]类型的左值int[3]

指针和数组通常可以被相同地处理,但是存在差异。 指针确实有一个内存位置,因此您可以获取指针的地址。 但是在运行时,数组没有指向它。 因此,对于编译器来说,获取数组的地址在语法上定义为与第一个元素的地址相同。 这是有道理的,大声朗读这句话。