Ответ 1
Они не то же самое. Они просто находятся в одном месте памяти. Например, вы можете написать arr+2
, чтобы получить адрес arr[2]
, но не (&arr)+2
, чтобы сделать то же самое.
Кроме того, sizeof arr
и sizeof &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
.
Они не то же самое. Они просто находятся в одном месте памяти. Например, вы можете написать arr+2
, чтобы получить адрес arr[2]
, но не (&arr)+2
, чтобы сделать то же самое.
Кроме того, sizeof arr
и sizeof &arr
отличаются.
#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
Как вы можете видеть на этой диаграмме, первый элемент массива имеет тот же адрес, что и сам массив.
Оба имеют одинаковое значение, но разные типы.
Когда он используется сам по себе (а не операнд &
или 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
) имя, очевидно, используется как операнд унарного оператора &
, поэтому преобразование не выполняется.
Адрес тот же, но оба выражения разные. Они только начинаются в том же месте памяти. Типы обоих выражений различны.
Значение arr
имеет тип int *
, а значение &arr
имеет тип int (*)[3]
.
&
- это адресный оператор, и адрес объекта является указателем на этот объект. Указатель на объект типа int [3]
имеет тип int (*)[3]
Они не совпадают.
Немного более строгое объяснение:
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]
.
Указатели и массивы часто могут обрабатываться одинаково, но есть различия. У указателя есть ячейка памяти, поэтому вы можете взять адрес указателя. Но массив не имеет ничего общего с ним во время выполнения. Поэтому, принимая адрес массива, для компилятора синтаксически определяется так же, как адрес первого элемента. Что имеет смысл, прочитав это предложение вслух.