为什么arr和&arr一样?
我已经编程了很多年的c / c ++,但今天的偶然发现让我有点好奇……为什么两个输出在下面的代码中产生相同的结果? ( arr
当然是arr[0]
的地址,即指向arr[0]
的指针。我本来期望&arr
是该指针的地址,但它与arr
具有相同的值)
int arr[3]; cout << arr << endl; cout << &arr << endl;
备注:这个问题已经结束,但现在又被打开了。 (谢谢 ?)
我知道&arr[0]
和arr
评估的数字相同,但这不是我的问题! 问题是为什么&arr
和arr
评估相同的数字。 如果arr
是文字(不存储任何软件),那么编译器应该抱怨并说arr
不是左值。 如果arr
的地址存储在某处,那么&arr
应该给我该位置的地址。 (但这种情况并非如此)
如果我写
const int * arr2 = arr;
那么对于任何整数i
, arr2[i]==arr[i]
,但是&arr2 != arr
。
他们不一样。 他们只是在同一个记忆位置。 例如,您可以编写arr+2
来获取arr[2]
的地址,而不是(&arr)+2
来执行相同的操作。
另外, sizeof arr
和sizeof &arr
是不同的。
#include struct foo { int x; int y; }; int main() { foo f; void* a = &f.x; void* b = &f; assert(a == b); }
出于同样的原因,上面的两个地址a
和b
是相同的 。 对象的地址与其第一个成员的地址相同(但它们的类型不同)。
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]
他们不一样。
更严格的解释:
arr
是int [3]
类型的左值 int [3]
。 尝试在某些表达式(如cout << arr
将导致lvalue-to-rvalue转换,因为没有数组类型的rvalues,它将转换为int *
类型的rvalue并且值等于&arr[0]
。 这是你可以显示的。
&arr
是int (*)[3]
类型的右值 ,指向数组对象本身。 这里没有魔法:-)这个指针指向与&arr[0]
相同的地址,因为数组对象及其第一个成员在内存中的完全相同的位置开始。 这就是打印时得到相同结果的原因。
确认它们不同的一种简单方法是比较*(arr)
和*(&arr)
:第一个是int
类型的左值,第二个是int[3]
类型的左值int[3]
。
指针和数组通常可以被相同地处理,但是存在差异。 指针确实有一个内存位置,因此您可以获取指针的地址。 但是在运行时,数组没有指向它。 因此,对于编译器来说,获取数组的地址在语法上定义为与第一个元素的地址相同。 这是有道理的,大声朗读这句话。