В языках Си/Cи++ считается, что массивы имеют только одну размерность. Многомерные массивы представляются массивами указателей на массивы (или массивами массивов).
Двумерные массивы как структуры данных на логическом уровне представляются матрицей значений. Массивы большей размерности представляются параллелепипедом (или набором матриц), «гиперпараллелепипедом» (набором наборов матриц) и т.д. В этой связи такие подобные струкутры данных иногда называют прямоугольными. Поскольку память ЭВМ представляет собой линейную (т.е., фактически, одномерную) последовательность ячеек с уникальными адресами, то на физическом уровне существует сложность представления массивов с размерностью большей единицы. Такие массивы представляются массивами одномерных массивов, фактически, массивами указателей на одномерные массивы.
В случае двумерного массива, т.е. матрицы, в каждом одномерном массиве такого массива указателей на массивы хранятся элементы столбцов или строк матрицы. Выбор столбцов или строк определяется языком программирования. Для языков Си/Си++ это будут строки. Для трехмерного массива параллелепипед раскладывается на слои, каждый из которых является матрицей. Реально, слой – это указатель на матрицу, все слои – массив указателей на матрицы. В свою очередь, матрица – массив указателей на векторы. Таким образом, для доступа к элементам многомерных (и одномерных) массивов могут использоваться указатели. Такой способ доступа будет рассмотрен позже.
В языке Си++ многомерные массивы определяются следующим образом.
int foxtrot[5][20];
Элементы 2-мерного массива хранятся по строкам, т.е. если проходить по ним в порядке их расположения в памяти, то быстрее всего изменяется крайний правый индекс. То же справедливо и для массивов большей размерности.
При инициализации многомерных массивов соблюдаются те же соглашения, что и при инициализации одномерных массиво, за исключением того, что можно не указывать размер только по первой (самой левой) размерности:
int b[][2]={{1,2},{3,4},{5,6}}; // b[3][2]
Если заданы все значения, то внутренние { } скобки могут отсутствовать.
int c[][2]={1,2,3,4,5,6};
int d[][2]={{1},{2},{3}};
Имя любого массива, как одно-, так и многомерного, само по себе является адресом начального (т.е. нулевого) элемента этого массива и, одновременно, константой. Т.е. константным указателем.
foxtrot ~ &foxtrot[0][0]
В таком качестве имя массива может использовать везде, где использовался бы константный указатель. Единственным исключением является использование имени массива в операции sizeof. Результатом операции в этом случае будет число байт, занимаемых всем массивом, а не указателем.
Многомерный динамический массив может быть создан по крайней мере двумя способами. Первый способ:
char (*ram1)[512]; // Указатель на символьный массив из 512 элементов
int SizeY = 40;
ram1 = new char [SizeY][512]; // Двумерный динамический символьный массив
В этом способе обязательно должен использоваться указатель на массив (1-, 2- или многомерный, в зависимости от конечной размерности) и только первый (самой левый) размер создаваемого массива может быть задан при помощи переменной.
long (*lp) [3][4];
lp = new long[2][3][4];
Удаляются такие динамические массивы также, как одномерные:
delete [] ram1;
delete [] lp;
Второй способ является более гибким и допускает использование переменных по всем размерностям.
int height = 256, width = 512; // Высота и ширина матрицы
unsigned char **ram; // Указатель на указатель (для 2-мерного массива)
ram=new unsigned char *[height]; // Создание 1-мерного массива указателей
int i;
for(i = 0; i<height; i++)
ram[i] = new unsigned char[width]; // Создание строк-одномерных массивов
Каждый одномерный массив-строка можнт быть неодинакового размера.
Удаление двумерного динамического массива, созданного таким способом, выполняется следующим образом: