Массивы распадаются до указателей на их первый элемент .
Так что, когда вы печатаете arr
, это действительно &arr[0]
.
И когда вы печатаете *arr
это действительно &arr[0][0]
.
И оба начинаются в одном и том же месте.
Легко увидеть, если мы посмотрим, как массивы будут в памяти:
+-----------+-----------+-----------+-----------+
| arr[0][0] | arr[0][1] | arr[1][0] | arr[1][1] |
+-----------+-----------+-----------+-----------+
^
|
&arr[0][0]
|
&arr[0]
|
&arr
Большая разница между указателями заключается не в их расположении, а в их типе : &arr[0][0]
относится к типу int *
;&arr[0]
относится к типу int (*)[2]
;И &arr
имеет тип int (*)[2][2]
.
При работе с этими вещами также полезно знать, что для любого массива или указателя a
и индекса i
выражение a[i]
точно равно *(a + i)
.
Вооружившись этим знанием, если мы возьмем, например, &arr[0]
, оно будет равно &(*(arr + 0))
.Это равно &(*arr)
, что равно arr
(потому что адрес оператора &
и оператор разыменования *
отменяют друг друга).
Как уже упоминалось arr[0]
также равно 0[arr]
.Это также происходит из эквивалентности arr[i]
и *(arr + i)
.
Из-за коммутативного свойства сложения выражение *(arr + i)
равно *(i + arr)
, что затем приводит кнечетный i[arr]
синтаксис.
Это любопытство, которое не следует делать в «реальном» коде.