R.8.2.4 Массивы
R.8.2.4 Массивы
В описании 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];
описан статический трехмерный массив целых размера 3?5?7. Строго говоря, 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];
Здесь описан массив из 3?5 целых. Если в выражении появляется x, то оно преобразуется в указатель на первый массив из пяти целых. Если в выражении появляется x[i], что эквивалентно *(x+i), в начале x преобразуется в указатель, как было сказано выше, затем x+i преобразуется к типу x, для чего необходимо i умножить на размер объекта, на который указывает x, т.е. на размер пяти целых. Затем происходит сложение и применяется косвенность, после чего получим массив (из пяти целых), который в свою очередь преобразуется в указатель на первое из целых. Если есть еще одна индексация, процесс повторяется, и на этот раз мы получим в результате целое.
Из всего этого следует, что массивы в C++ хранятся по строкам (последний индекс изменяется быстрее всего), а значение первого индекса из описания позволяет вычислить размер памяти, необходимой для массива, однако при вычислении индексного выражения первый индекс роли не играет.