我从事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* arr = arr;
然后arr2[i]==arr[i]
对于任何整数i
,但&arr2 != arr
。
7条答案
按热度按时间k75qkfdt1#
出于同样的原因,两个地址
a
和b
以上are the same。对象的地址与其第一个成员的地址相同(但它们的类型不同)。正如你在这个图中看到的,数组的第一个元素与数组本身位于同一地址。
00jrzges2#
他们不一样它们只是在同一个内存位置。例如,你可以写
arr+2
来获取arr[2]
的地址,但不能写(&arr)+2
来做同样的事情。另外,
sizeof arr
和sizeof &arr
是不同的。6fe3ivhb3#
两者具有相同的值,但类型不同。
当它被单独使用时(不是
&
或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”的表达式将转换为类型为“pointer to type”的表达式,该类型指向数组对象的初始元素,并且不是左值。
[note:这段话来自C99标准,但我相信在C和C++标准的所有版本中都有等效的语言。
在第一种情况下(
arr
本身),arr
没有被用作sizeof,unary &等的操作数,所以它被转换(不升级)为类型“指针类型”(在这种情况下,“指针int”)。在第二种情况下(
&arr
),名称显然 * 被用作一元&
运算符的操作数-因此转换不会发生。z9ju0rcb4#
地址相同,但两种表达方式不同。它们只是从同一个记忆位置开始。这两种表达的类型是不同的。
arr
的值是int *
类型,&arr
的值是int (*)[3]
类型。&
是地址操作符,对象的地址是指向该对象的指针。指向int [3]
类型对象的指针的类型为int (*)[3]
i2byvkas5#
他们不一样。
更严格一点的解释:
arr
是int [3]
类型的左值。尝试在某些表达式(如cout << arr
)中使用arr
将导致左值到右值的转换,因为没有数组类型的右值,将其转换为int *
类型的右值,并且值等于&arr[0]
。这就是你可以展示的。&arr
是int (*)[3]
类型的右值,指向数组对象本身。这里没有魔法:-)这个指针指向与&arr[0]
相同的地址,因为数组对象和它的第一个成员在内存中完全相同的位置开始。这就是为什么打印它们时会有相同的结果。确认它们不同的一个简单方法是比较
*(arr)
和*(&arr)
:第一个是int
类型的左值,第二个是int[3]
类型的左值。klsxnrf16#
指针和数组通常可以被等同对待,但也有区别。指针有一个内存位置,所以你可以获取指针的地址。但是在运行时,数组没有任何东西指向它。因此,对于编译器来说,获取数组的地址在语法上定义为与第一个元素的地址相同。这就说得通了,阅读这句话。
6ojccjat7#
我发现Graham Perks的回答非常有见地,我甚至在一个在线编译器中测试了这个答案:
输出:
address of arr: 0x7ffed83efbac
address of arrPointer: 0x7ffed83efba0
arr: 0x7ffed83efbac
arrPointer: 0x7ffed83efbac
*arr: 1
*arrPointer: 1
似乎混淆是因为arr和arrPointer是等价的。然而,正如格雷厄姆·帕克斯在他的回答中所详述的那样,他们不是。
从视觉上看,内存看起来像这样:
[Memory View]
[memory address: value stored]
arrPointer:
0x7ffed83efba0: 0x7ffed83efbac
arr:
0x7ffed83efbac: 1
0x7ffed83efbb0: 2
0x7ffed83efbb4: 3
正如你所看到的,
arrPointer
是内存地址0x7ffed83efba0
的标签,它有4个字节的已分配内存,其中包含了内存地址0x7ffed83efba0
[0]。另一方面,
arr
是内存地址0x7ffed83efbac
的标签,根据Jerry Coffin的回答,由于变量arr
的类型是“类型数组”,它被转换为“类型指针”(指向数组的起始地址),因此打印arr
会产生0x7ffed83efbac
。关键的区别是
arrPointer
是一个实际的指针,并且有自己的内存槽来保存它所指向的内存的值,所以&arrPointer != arrPointer
。由于arr
不是技术上的指针,而是一个数组,所以我们在打印arr
时看到的内存地址不是存储在其他地方,而是由上面提到的转换确定的。因此,&arrPointer
和arrPointer
的值(而不是类型)是相等的。