Вот точный язык из стандарта C ( n1256 ):
6.3.2.1 L-значения, массивы и обозначения функций
...
3 За исключением случаев, когда это операнд оператора sizeof
или унарный оператор &
или строковый литерал, используемый для инициализации массива, выражение имеет тип '' array of type '' преобразуется в выражение с указателем типа '' на тип '', которое указывает на начальный элемент объекта массива и не является lvalue. Если объект массива имеет класс хранения регистров, поведение не определено.
Здесь важно помнить, что существует разница между объектом (в терминах C, означающим что-то, что занимает память) и выражением , используемым для обозначения этого объект. * * тысяча двадцать-один
Когда вы объявляете массив, такой как
int a[10];
объект , обозначенный выражением a
- это массив (т. Е. Непрерывный блок памяти, достаточно большой для хранения значений 10 int
), и тип выражение a - это "10-элементный массив int
" или int [10]
. Если выражение a
появляется в контексте, отличном от операнда операторов sizeof
или &
, то его тип неявно преобразуется в int *
, а его значением является адрес первый элемент.
В случае оператора sizeof
, если операнд является выражением типа T [N]
, то результатом будет число байтов в объекте массива, а не указатель на этот объект: N * sizeof T
.
В случае оператора &
значением является адрес массива, который совпадает с адресом первого элемента массива, но тип выражения равен отличается: учитывая объявление T a[N];
, тип выражения &a
равен T (*)[N]
или указатель на массив из N элементов T. Значение совпадает с a
или &a[0]
(адрес массива совпадает с адресом первого элемента в массиве), но разница в типах имеет значение. Например, учитывая код
int a[10];
int *p = a;
int (*ap)[10] = &a;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
p++;
ap++;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
вы увидите вывод порядка
p = 0xbff11e58, ap = 0xbff11e58
p = 0xbff11e5c, ap = 0xbff11e80
IOW, продвижение p
добавляет sizeof int
(4) к исходному значению, тогда как продвижение ap
добавляет 10 * sizeof int
(40).
Более стандартный язык:
6.5.2.1 Массив подписки
Ограничения
1 Одно из выражений должно иметь указатель типа '' на объект тип '', другое выражение должно иметь целочисленный тип, а результат имеет тип '' type ''.
Семантика
2 Постфиксное выражение, за которым следует выражение в квадратных скобках []
- это подписанное обозначение элемента объекта массива. Определение индекса []
состоит в том, что E1[E2]
идентичен (*((E1)+(E2)))
. Из-за правил преобразования, которые применяются к двоичному оператору +
, если E1
является объектом массива (эквивалентно указателю на начальный элемент объекта массива) и E2
является целым числом, E1[E2]
обозначает E2
-й элемент E1
(считая с нуля).
Таким образом, когда вы подписываете выражение массива, под капотом происходит то, что вычисляется смещение от адреса первого элемента в массиве, а результат разыменовывается. Выражение
a[i] = 10;
эквивалентно
*((a)+(i)) = 10;
, что эквивалентно
*((i)+(a)) = 10;
, что эквивалентно
i[a] = 10;
Да, подписка на массив в C является коммутативной; ради любви к Богу, никогда не делайте этого в коде производства.
Поскольку подписка массива определяется в терминах операций с указателями, вы можете применить оператор индекса для выражений типа указателя, а также типа массива:
int *p = malloc(sizeof *p * 10);
int i;
for (i = 0; i < 10; i++)
p[i] = some_initial_value();
Вот удобная таблица, чтобы запомнить некоторые из этих понятий:
Declaration: T a[N];
Expression Type Converts to Value
---------- ---- ------------ -----
a T [N] T * Address of the first element in a;
identical to writing &a[0]
&a T (*)[N] Address of the array; value is the same
as above, but the type is different
sizeof a size_t Number of bytes contained in the array
object (N * sizeof T)
*a T Value at a[0]
a[i] T Value at a[i]
&a[i] T * Address of a[i]
Declaration: T a[N][M];
Expression Type Converts to Value
---------- ---- ------------ -----
a T [N][M] T (*)[M] Address of the first subarray (&a[0])
&a T (*)[N][M] Address of the array (same value as
above, but different type)
sizeof a size_t Number of bytes contained in the
array object (N * M * sizeof T)
*a T [M] T * Value of a[0], which is the address
of the first element of the first subarray
(same as &a[0][0])
a[i] T [M] T * Value of a[i], which is the address
of the first element of the i'th subarray
&a[i] T (*)[M] Address of the i-th subarray; same value as
above, but different type
sizeof a[i] size_t Number of bytes contained in the i'th subarray
object (M * sizeof T)
*a[i] T Value of the first element of the i'th
subarray (a[i][0])
a[i][j] T Value at a[i][j]
&a[i][j] T * Address of a[i][j]
Declaration: T a[N][M][O];
Expression Type Converts to
---------- ---- -----------
a T [N][M][O] T (*)[M][O]
&a T (*)[N][M][O]
*a T [M][O] T (*)[O]
a[i] T [M][O] T (*)[O]
&a[i] T (*)[M][O]
*a[i] T [O] T *
a[i][j] T [O] T *
&a[i][j] T (*)[O]
*a[i][j] T
a[i][j][k] T
Отсюда шаблон для многомерных массивов должен быть четким.
Итак, в итоге: массивыне указатели. В большинстве случаев выражения массива преобразуются в типы указателей.