|
|
В описании T D, в котором D имеет вид
D1 [ выражение-константа opt ]
описывается идентификатор типа " ... массив T". Если
выражение-константа присутствует ($$R.5.19), то оно должно иметь
целочисленный тип и значение, большее 0. Это выражение задает число
элементов массива. Если значение выражения-константы есть N, то
массив имеет N элементов с индексами от 0 до N-1.
Массив можно образовывать из: одного из основных типов (за исключением
void), указателя, указателя на члены, класса, перечисления или из
другого массива.
Если подряд идут несколько спецификаций "массив ...", образуется
многомерный массив, причем выражение-константа, задающее границы
массива, может отсутствовать только для первого массива. Такое умолчание
полезно в случае параметров функции типа массив, а также когда массив
является внешним, а его определение, с которым связано резервирование
памяти, находится в другом месте. Первое выражение-константа может
быть пропущено и в том случае, если за описателем следует
список-инициализаторов ($$R.8.4). Тогда размер массива определяется
числом элементов, приведенных в инициализаторе ($$R.8.4.1).
В описании
float fa[17], *afp[17];
описаны массив чисел типа float и массив указателей на числа типа float, а в описании
static int x3d[3][5][7];
описан статический трехмерный массив целых размера 3x5x7. Строго
говоря, x3d является массивом из трех элементов, каждый из которых
есть массив из пяти массивов, а каждый из последних является массивом
из семи целых. В выражении допустимо появление любого из следующих
выражений: x3d, x3d[i], x3d[i][j], x3d[i][j][k].
Если в выражении участвует идентификатор типа массив, то, исключая
случаи операнда в операциях sizeof или & и инициализатора для
ссылки ($$R.8.4.3), его тип преобразуется в указатель на первый
элемент массива. Несмотря на это преобразование, массивы не являются
изменяемыми адресами. Если не считать случай использования массива
при описании класса ($$R.13.4.5), операция индексации определяется
так, что E1[E2] совпадает с *((E1) + (E2)). С учетом правил
преобразования типов для операции +, если E1 есть массив, а E2
целое, то E1[E2] указывает на E2-элемент из E1. Поэтому, несмотря
на свой асиметричный вид, индексация - коммутативная операция.
Аналогичное правило действует и для многомерных массивов. Если
E - n-мерный массив размера ixjx...xk, то в выражении он
преобразуется в указатель на (n-1)-мерный массив размера jx...xk.
Если к этому указателю явно или неявно в результате индексации применяется
операция *, указуемый (n-1)-мерный массив сам немедленно преобразуется
в указатель.
Например, рассмотрим описание
int x[3][5];
Здесь описан массив из 3x5 целых. Если в выражении появляется x, то
оно преобразуется в указатель на первый массив из пяти целых.
Если в выражении появляется x[i], что эквивалентно *(x+i), в начале
x преобразуется в указатель, как было сказано выше, затем x+i
преобразуется к типу x, для чего необходимо i умножить на размер объекта,
на который указывает x, т.е. на размер пяти целых. Затем происходит
сложение и применяется косвенность, после чего получим массив (из пяти
целых), который в свою очередь преобразуется в указатель на первое из
целых. Если есть еще одна индексация, процесс повторяется, и на этот раз
мы получим в результате целое.
Из всего этого следует, что массивы В С++ хранятся по строкам
(последний индекс изменяется быстрее всего), а значение первого
индекса из описания позволяет вычислить размер памяти, необходимой
для массива, однако при вычислении индексного выражения первый индекс
роли не играет.
| |