Если выражение типа массива (например, имя массива) появляется в большем выражении и не является операндом операторов &
или sizeof
, то тип выражения массива преобразуется из «N-элемент массива из T» в «указатель на T», а значением выражения является адрес первого элемента в массиве.
Короче говоря, имя массива не является указателем, но в большинстве случаев оно обрабатывается , как если бы было указателем.
Редактировать
Отвечая на вопрос в комментарии:
Если я использую sizeof, могу ли я считать размер только элементов массива? Тогда массив «head» также занимает место с информацией о длине и указателем (а это значит, что он занимает больше места, чем обычный указатель)?
Когда вы создаете массив, единственным местом, которое выделяется, является пространство для самих элементов; не хранится хранилище для отдельного указателя или метаданных. Учитывая
char a[10];
что вы получаете в памяти
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
...
+---+
| | a[9]
+---+
Выражение a
относится ко всему массиву, но объект a
не отделен от самих элементов массива. Таким образом, sizeof a
дает вам размер (в байтах) всего массива. Выражение &a
дает вам адрес массива, , который совпадает с адресом первого элемента . Разница между &a
и &a[0]
заключается в типе результата 1 - char (*)[10]
в первом случае и char *
во втором.
Когда все становится странным, когда вы хотите получить доступ к отдельным элементам - выражение a[i]
определяется как результат *(a + i)
- с учетом значения адреса a
, смещение i
элементов ( не байт) ) с этого адреса и разыменуйте результат.
Проблема в том, что a
- это не указатель или адрес, а весь объект массива. Таким образом, правило в C гласит, что всякий раз, когда компилятор видит выражение типа массива (например, a
, имеющее тип char [10]
) и , это выражение не является операндом sizeof
или унарные операторы &
, тип этого выражения преобразуется («распадается») в тип указателя (char *
), а значением выражения является адрес первого элемента массива. Поэтому выражение a
имеет тот же тип и значение, что и выражение &a[0]
(и, соответственно, выражение *a
имеет тот же тип и значение, что и выражение a[0]
).
C был получен из более раннего языка, называемого B, и в B a
был отдельным объектом указателя из элементов массива a[0]
, a[1]
и т. Д. Ричи хотел сохранить массив B семантика, но он не хотел возиться с хранением отдельного объекта указателя. Таким образом, он избавился от этого. Вместо этого компилятор будет преобразовывать выражения массива в выражения указателя во время перевода по мере необходимости.
Помните, что я сказал, что в массивах не хранятся метаданные об их размере. Как только это выражение массива «распадается» на указатель, все, что у вас есть, - это указатель на один элемент. Этот элемент может быть первым из последовательности элементов или может быть отдельным объектом. Там нет никакого способа узнать, основываясь на самом указателе.
Когда вы передаете выражение массива в функцию, все, что получает функция, это указатель на первый элемент - он не знает, насколько большой массив (именно поэтому функция gets
была такой угрозой и в конечном итоге была удален из библиотеки). Чтобы функция знала, сколько элементов в массиве, вы должны либо использовать значение часового (например, терминатор 0 в строках C), либо вы должны передать количество элементов в качестве отдельного параметра.
- Что * может * влиять на интерпретацию значения адреса - зависит от аппарата.