Подтвердить что ты не робот

Почему arr и & arr то же самое?

Я программировал c/С++ в течение многих лет, но сегодняшнее случайное открытие заставило меня несколько любопытно... Почему оба вывода дают одинаковый результат в коде ниже? (arr - это, конечно, адрес arr[0], то есть указатель на arr[0]. Я бы ожидал, что &arr будет адресом этого указателя, но имеет то же значение, что и arr)

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

Примечание. Этот вопрос был закрыт, но теперь он снова открывается. (Спасибо?)

Я знаю, что &arr[0] и arr оценивается на один и тот же номер, но это не мой вопрос! Возникает вопрос, почему &arr и arr оцениваются на один и тот же номер. Если arr является литералом (не сохраняется), компилятор должен жаловаться и сказать, что arr не является значением lvalue. Если адрес arr хранится где-то, тогда &arr должен указать мне адрес этого места. (но это не так)

если я пишу

const int * arr2 = arr;

то arr2[i]==arr[i] для любого целого i, но &arr2 != arr.

4b9b3361

Ответ 1

Они не то же самое. Они просто находятся в одном месте памяти. Например, вы можете написать arr+2, чтобы получить адрес arr[2], но не (&arr)+2, чтобы сделать то же самое.

Кроме того, sizeof arr и sizeof &arr отличаются.

Ответ 2

#include <cassert>

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

Как вы можете видеть на этой диаграмме, первый элемент массива имеет тот же адрес, что и сам массив.

Ответ 3

Оба имеют одинаковое значение, но разные типы.

Когда он используется сам по себе (а не операнд & или sizeof), arr оценивает указатель на int, содержащий адрес первого int в массиве. &arr оценивает указатель на массив из трех int s, удерживая адрес массива. Поскольку первый int в массиве должен находиться в самом начале массива, эти адреса должны быть равны.

Разница между двумя становится очевидной, если вы делаете некоторую математику по результатам:

arr+1 будет равно arr + sizeof(int).

((&arr) + 1) будет равно arr + sizeof(arr) == arr + sizeof(int) * 3

Изменить. Что касается того, как/почему это происходит, ответ довольно прост: поскольку стандарт говорит об этом. В частности, он говорит (§6.3.2.1/3):

За исключением случаев, когда это операнд оператора sizeof или унарный и оператор, или     строковый литерал, используемый для инициализации массива, выражение, которое имеет тип '' массив типа, преобразуется в выражение с типом '' указателем на тип, указывающим на начальный элемент объекта массива и не является lvalue.

[примечание: эта конкретная цитата относится к стандарту C99, но я считаю, что там эквивалентный язык во всех версиях как для C, так и для С++).

В первом случае (arr сам по себе) arr не используется как операнд sizeof, unary & и т.д., поэтому он преобразуется (не продвигается) в тип "указатель на тип" "(в этом случае," указатель на int ").

Во втором случае (&arr) имя, очевидно, используется как операнд унарного оператора &, поэтому преобразование не выполняется.

Ответ 4

Адрес тот же, но оба выражения разные. Они только начинаются в том же месте памяти. Типы обоих выражений различны.

Значение arr имеет тип int *, а значение &arr имеет тип int (*)[3].

& - это адресный оператор, и адрес объекта является указателем на этот объект. Указатель на объект типа int [3] имеет тип int (*)[3]

Ответ 5

Они не совпадают.

Немного более строгое объяснение:

arr - lvalue типа int [3]. Попытка использования arr в некоторых выражениях, таких как cout << arr, будет получено преобразование lvalue-to-rvalue, которое, поскольку не существует rvalues ​​типа массива, преобразует его в rvalue типа int * и со значением, равным &arr[0], Это то, что вы можете отобразить.

&arr является rvalue типа int (*)[3], указывая на сам объект массива. Здесь нет волшебства:-) Этот указатель указывает на тот же адрес, что и &arr[0], потому что объект массива и его первый член начинаются в том же месте в памяти. Вот почему у вас есть тот же результат при их печати. ​​


Легкий способ подтвердить, что они отличаются друг от друга, заключается в сравнении *(arr) и *(&arr): первый - это lvalue типа int, а второй - lvalue типа int[3].

Ответ 6

Указатели и массивы часто могут обрабатываться одинаково, но есть различия. У указателя есть ячейка памяти, поэтому вы можете взять адрес указателя. Но массив не имеет ничего общего с ним во время выполнения. Поэтому, принимая адрес массива, для компилятора синтаксически определяется так же, как адрес первого элемента. Что имеет смысл, прочитав это предложение вслух.