C语言 为什么&是一样的?

qxsslcnc  于 12个月前  发布在  其他
关注(0)|答案(7)|浏览(132)

我从事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* arr = arr;
然后arr2[i]==arr[i]对于任何整数i,但&arr2 != arr

k75qkfdt

k75qkfdt1#

#include <cassert>

struct foo {
    int x;
    int y;
};

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

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

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

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

00jrzges

00jrzges2#

他们不一样它们只是在同一个内存位置。例如,你可以写arr+2来获取arr[2]的地址,但不能写(&arr)+2来做同样的事情。
另外,sizeof arrsizeof &arr是不同的。

6fe3ivhb

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),名称显然 * 被用作一元&运算符的操作数-因此转换不会发生。

z9ju0rcb

z9ju0rcb4#

地址相同,但两种表达方式不同。它们只是从同一个记忆位置开始。这两种表达的类型是不同的。
arr的值是int *类型,&arr的值是int (*)[3]类型。
&是地址操作符,对象的地址是指向该对象的指针。指向int [3]类型对象的指针的类型为int (*)[3]

i2byvkas

i2byvkas5#

他们不一样。
更严格一点的解释:
arrint [3]类型的左值。尝试在某些表达式(如cout << arr)中使用arr将导致左值到右值的转换,因为没有数组类型的右值,将其转换为int *类型的右值,并且值等于&arr[0]。这就是你可以展示的。
&arrint (*)[3]类型的右值,指向数组对象本身。这里没有魔法:-)这个指针指向与&arr[0]相同的地址,因为数组对象和它的第一个成员在内存中完全相同的位置开始。这就是为什么打印它们时会有相同的结果。
确认它们不同的一个简单方法是比较*(arr)*(&arr):第一个是int类型的左值,第二个是int[3]类型的左值。

klsxnrf1

klsxnrf16#

指针和数组通常可以被等同对待,但也有区别。指针有一个内存位置,所以你可以获取指针的地址。但是在运行时,数组没有任何东西指向它。因此,对于编译器来说,获取数组的地址在语法上定义为与第一个元素的地址相同。这就说得通了,阅读这句话。

6ojccjat

6ojccjat7#

我发现Graham Perks的回答非常有见地,我甚至在一个在线编译器中测试了这个答案:

int main()
    {

    int arr[3] = {1,2,3};
    int *arrPointer = arr; // this is equivalent to: int *arrPointer = &arr;
    
    printf("address of arr: %p\n", &arr);
    printf("address of arrPointer: %p\n", &arrPointer);
    
    printf("arr: %p\n", arr);
    printf("arrPointer: %p\n", arrPointer);
    
    printf("*arr: %d\n", *arr);
    printf("*arrPointer: %d\n", *arrPointer);

    return 0;
    }

输出:
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时看到的内存地址不是存储在其他地方,而是由上面提到的转换确定的。因此,&arrPointerarrPointer的值(而不是类型)是相等的。

相关问题