Ответ 1
Здесь точный язык из стандарта C (n1256):
6.3.2.1 Lvalues, массивы и обозначения функций
...
3 За исключением случаев, когда он является операндом оператораsizeof
или унарным оператором&
или является строковым литералом, используемым для инициализации массива, выражение, которое имеет массив типов '' типа, преобразуется в выражение с типом ' 'указатель на тип, который указывает на исходный элемент объекта массива и не является значением lvalue. Если объект массива имеет класс хранения регистров, поведение undefined.
Важно помнить, что существует разница между объектом (в терминах 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 Одно из выражений должно иметь тип '' указатель на тип объекта, другое выражение должно иметь целочисленный тип, а результат имеет тип типа '. <Б > Семантика
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
Здесь шаблон четких массивов должен быть ясным.
Итак, в итоге: массивы не являются указателями. В большинстве контекстов выражения массива преобразуются в типы указателей.