Описание массивов

Описание массивов наследует некоторые черты описания переменных. Прежде всего следует отметить, что массивы могут быть глобальными и локальными в зависимости от места их объявления. Также по аналогии с переменными, при описании массива могут быть использованы модификаторы const и static. Для одномерного фиксированного массива синтаксис объявления выглядит следующим образом:

type static1D[size];

Здесь, type и static1D обозначают название типа элементов и идентификатор массива соответственно, а size в квадратных скобках — это целочисленная константа, задающая размер.

Для многомерных массивов необходимо указывать несколько размеров, по количеству измерений:

type static2D[size1][size2];
type static3D[size1][size2][size3];
type static4D[size1][size2][size3][size4];

Динамические массивы описываются аналогично, за исключением того, что в первых квадратных скобках делается пропуск (перед использованием такого массива нужно выделить под него требуемый объем памяти с помощью функции ArrayResize, см. раздел про динамические массивы).

type dynamic1D[];
type dynamic2D[][size2];
type dynamic3D[][size2][size3];
type dynamic4D[][size2][size3][size4];

Для фиксированных массивов разрешается делать инициализацию: начальные значения для элементов указываются после знака равно, списком через запятую, причем весь список заключается в фигурные скобки. Например:

int array1D[3] = {102030};

Здесь целочисленный массив размером 3 получает значения 10, 20, 30.

При наличии списка инициализации необязательно указывать размер массива в квадратных скобках (для первого измерения) — компилятор автоматически определит размер по длине списка. Например:

int array1D[] = {102030};

Начальными значениями могут быть не только константы, но и константные выражения — формулы, которые компилятор способен рассчитать в момент компиляции. Например, следующий массив заполняется количеством секунд в минуте, часе, сутках и неделе (представление в виде формул более наглядное, чем 86400 или 604800):

int seconds[] = {60, 60 * 60, 60 * 60 * 24, 60 * 60 * 24 * 7};

Обычно такие значения оформляются как макрос препроцессора в начале кода, и потом везде, где нужно по тексту, вместо формул вставляется имя макроса. Эта возможность описана в разделе Препроцессор.

Количество инициализирующих элементов не должно превышать размер массива. Иначе компилятор выдаст ошибку "слишком много инициализаторов" ("too many initializers"). Если количество значений меньше размера массива, оставшиеся элементы инициализируются нулем. Поэтому существует сокращенная нотация для инициализации всего массива нулями:

int array2D[2][3] = {0};

Или просто пустые скобки:

int array2D[2][3] = {};

Она работает вне зависимости от количества измерений.

Для инициализации многомерных массивов списки значений должны быть вложенными. Например:

int array2D[3][2] = {{1, 2}, {3, 4}, {5, 6}};

Здесь массив имеет размер 3 по первому измерению, поэтому внутри внешних фигурных скобок две запятых выделяют 3 элемента. Однако поскольку массив двумерный, каждый из его элементов — это в свою очередь массив, причем размер каждого из них равен 2. Поэтому каждый элемент представляет собой список в фигурных скобках, в каждом списке по 2 значения.

Если, предположим, нам требуется транспонированный массив (первый размер равен 2, второй — 3), то его инициализация изменится:

int array2D[2][3] = {{1, 3, 5}, {2, 4, 6}};

При необходимости можно пропустить одно или более значений в списке инициализации, обозначив их место запятыми. Все пропущенные элементы будут также проинициализированы нулем.

int array1D[3] = {, , 30};

Здесь первые два элемента будут равны 0.

Синтаксисом языка разрешено ставить запятую и после последнего элемента:

string messages[] =
{
  "undefined",
  "success",
  "error",
};

Это упрощает добавление новых элементов, особенно при многострочной записи. В частности, если забыть вставить запятую перед новым добавленным элементом в строковом массиве, окажется, что старая и новая строки склеятся в одном элементе (с прежним индексом), а нового элемента не появится. Кроме того, какие-то массивы могут генерироваться автоматически (другой программой или макросами), поэтому унифицированный вид всех элементов получается естественным.

"Куча" и "стек"
 
В случае массивов, которые могут потенциально иметь большие размеры, важно отметить разницу между глобальным и локальным размещением в памяти.
 
Под глобальные переменные и массивы память распределяется в так называемой куче — свободной памяти, доступной программе. Эта память практически ничем не ограничена кроме физических характеристик компьютера и операционной системы. Название "куча" объясняется тем, что участки памяти разных размеров постоянно то выделяются, то освобождаются программой, в результате чего свободные области оказываются произвольным образом разбросаны в общей массе.
 
Локальные переменные и массивы размещаются в стеке — это ограниченный участок памяти, заранее выделяемый программе специально для локальных элементов. Название стек происходит от того, что в процессе выполнения алгоритма часто происходят вложенные вызовы функций, накапливающие свои внутренние данные по принципу "нагромождения друг на друга": например, OnStart вызывается терминалом, из OnStart вызывается некая функция из вашего прикладного кода, из неё, в свою очередь, вызывается другая ваша функция и так далее. При этом в момент входа в каждую из функций на стеке создаются её локальные переменные, и они продолжают там оставаться, когда происходит вызов вложенной функции. Та тоже создает локальные переменные, которые попадают на стек как бы сверху предыдущих. В результате стек обычно содержит несколько слоев локальных данных из всех функций, которые активировались на пути к текущей строчке кода. Только когда функция, находящаяся на вершине стека, завершается, её локальные данные оттуда удаляются. В общем, стек — это хранилище, работающее по схеме FILO/LIFO (First In Last Out, Last In First Out).
 
Поскольку размер стека ограничен, в нем рекомендуется создавать только локальные переменные. Массивы же могут быть достаточно большими, чтобы быстро исчерпать весь стек. При этом выполнение программы завершается с ошибкой. Поэтому массивы следует описывать на глобальном уровне, статическими (static) или выделять под них память динамически (это тоже делается из кучи).