Почему "& массив" и "массив" указывают на один и тот же адрес?

До сих пор я думал, что массив такой же, как указатель. Но я обнаружил странный случай:

код

int array[5] = { 10,11,12,13,14};

std::cout << array << std::endl;
std::cout << &array << std::endl;
std::cout << &array[0] << std::endl;

int *pArray = new int[5];

std::cout << pArray << std::endl;
std::cout << &pArray << std::endl;
std::cout << &pArray[0] << std::endl;

выход

0x7ffeed730ad0
0x7ffeed730ad0
0x7ffeed730ad0

0x7f906d400340
0x7ffeed730a30
0x7f906d400340

Как вы можете видеть, array и &array имеют одинаковое значение. Но pArray и &pArray имеют разное значение. Если массив совпадает с указателем, адрес массива должен отличаться от массива. Как array и &array могут быть одинаковыми? Если array и &array совпадают, каков адрес памяти, в которой хранятся значения массива?

Ответ 1

Обычный array затухает до указателя на его первый элемент, он равен &array[0]. Первый элемент также начинается с того же адреса, что и сам массив. Отсюда &array == &array[0].

Но важно отметить, что типы разные:

  • Тип &array[0] (в вашем примере) int*.
  • Тип &array - int(*)[5].

Отношения между &array[0] и &array могут быть проще, если я покажу это немного "графически" (с добавленными указателями):

+----------+----------+----------+----------+----------+
| array[0] | array[1] | array[2] | array[3] | array[4] |
+----------+----------+----------+----------+----------+
^
|
&array[0]
|
&array

В качестве дополнительного приложения, array затухает до указателя на его первый элемент, то есть array затухает до &array[0] и, таким образом, будет иметь тот же тип.


Вещи разные с указателями, хотя. Указатель pArray указывает на некоторую память, значение pArray является местоположением этой памяти. Это то, что вы получаете, когда используете pArray. Это также как &pArray[0].

Когда вы используете &pArray, вы получаете указатель на указатель. То есть вы получаете местоположение (адрес) самой переменной pArray. Его тип - int**.

Несколько графически с указателем pArray было бы что-то вроде этого

+--------+       +-----------+-----------+-----------+-----------+-----------+-----+
| pArray | ----> | pArray[0] | pArray[1] | pArray[2] | pArray[3] | pArray[4] | ... |
+--------+       +-----------+-----------+-----------+-----------+-----------+-----+
^                ^
|                |
&pArray          &pArray[0]

[Обратите внимание на ... в конце "массива", потому что указатели не сохраняют информацию о памяти, на которую он указывает. Указатель указывает только на определенное место, "первый" элемент "массива". Обрабатывать память как "массив" - дело программиста.]

Ответ 2

Массив X должен вести себя как указатель на непрерывный список X в памяти так же, как указатель. Однако нигде не написано, где память, в которой хранятся эти данные, должна иметь собственный адрес и возможность записи. В случае явного указателя существует новое выделение для этого адреса (в данном случае стека), однако для массива в стеке компилятор уже знает, где находится содержимое, поэтому новое выделение не требуется.

Как следствие, небезопасно рассматривать его как указатель без индексации. например:

pArray = nullptr; // This is a memory leak, unless a copy is taken, but otherwise fine.
array = nullptr; // This is will make the compiler upset