Двумерные массивы

Помимо одномерных массивов вам может понадобиться для работы использование многомерного массива (двумерного, трёхмерного…). В этом уроке будут рассмотрены двумерные массивы. Они самые распространенные, а остальные встречаются крайне редко.

Мы уже рассматривали в предыдущих статьях одномерные массивы и Си-строки (символьные массивы).Там говорилось, что элементы массива размещаются в памяти последовательно — элемент за элементом. Визуально их можно представить в виде одной строки данных в памяти. Чтобы обратиться к какому-либо элементу такого массива, достаточно указать его имя и индекс элемента. Первое отличие двумерного массива от одномерного — его элементы содержат два индекса: int arr [3][4]; Данные такого массива можно представить, как таблицу: 3 х 4.

Первый за именем массива индекс — это индекс строки, второй — индекс столбца.

Когда вы уже посмотрели на эти рисунки, можно сказать о двумерном массиве так — это массив, в котором каждый элемент также является массивом. int arr [3][4]; — это массив из 3-х элементов, каждый из которых это массив из 4-х элементов.

Данные двумерного массива также располагаются в памяти последовательно, но построчно. Сначала строка с индексом 0 — ячейки от 0-й до 3-й, далее строка с индексом 1 — ячейки от 0-й до 3-й …

Что могут хранить элементы двумерных массивов? Например, можно хранить номера парковочных мест в многоэтажном паркинге (6 этажей и на каждом 15 мест для парковки). Для этого надо объявить двумерный массив int floorsAndParkings[6][15]; и записать в его ячейки номера мест на каждом этаже. Двумерный массив может хранить Си-строки. Например: char someStr [3][256];Так мы объявили массив, который будет хранить 3 строки по 256 символов каждая.

Инициализация двумерного массива.

Записать данные в двумерный массив можно при его объявлении. Рассмотрим на примере с местами парковки. Допустим в паркинге 2 этажа по 4 места парковки на каждом. Объявим массив и инициализируем его:

int floorsAndParkings[2][4] = { { 1, 2, 3, 4 }, { 1, 2, 3, 4 } };

Чтобы такая инициализация выглядела более читабельно, оформим её так:
int floorsAndParkings[2][4]
{
{ 1, 2, 3, 4 }, // инициализация floorsAndParkings[0]
{ 1, 2, 3, 4 }  // инициализация floorsAndParkings[1]
};
Как вы помните, согласно стандарту C++11, знак = можно упустить. Строки инициализируются по тому же принципу:
char someStr[3][16]
{
"Двумерные ",
"массивы ",
"в С++!"
};
Как вывести на экран данные двумерного массива? Можно пойти длинным путём и обращаться к каждому элементу вручную:
#include <iostream>
using namespace std;

int main()
{
int floorsAndParkings[2][4]
{
{ 1, 2, 3, 4 },
{ 1, 2, 3, 4 }  
};

cout << floorsAndParkings[0][0] << " ";
cout << floorsAndParkings[0][1] << " ";
cout << floorsAndParkings[0][2] << " ";
cout << floorsAndParkings[0][3] << " ";
cout << endl;

cout << floorsAndParkings[1][0] << " ";
cout << floorsAndParkings[1][1] << " ";
cout << floorsAndParkings[1][2] << " ";
cout << floorsAndParkings[1][3] << " ";
cout << endl;

return 0;
}

Вывод Си-строк двумерного массива на экран немного легче, так как нам достаточно указать только имя массива и индекс строки. Далее выходной поток cout самостоятельно будет выводить все элементы символьного массива, пока не обнаружит '\0'
#include <iostream>
using namespace std;

int main()
{
setlocale(LC_ALL, "rus");

char someStr[3][16]
{
"Двумерные ",
"массивы ",
"в С++!\n"
};

cout << someStr[0];
cout << someStr[1];
cout << someStr[2];

return 0;
}


Хорошо! А если нам надо заполнить и показать данные массиваint floorsAndParkings[20][100] или char someStr[50][256]? Эту неблагодарную работу можно в десятки раз облегчить, используя циклы. Точнее вложенные циклы.

Рассмотрим пример с паркингом. Показать пользователю схему паркинга: этажи и места для парковки. Чтобы забронировать место он должен выбрать номер этажа и номер места. После бронирования — записать значение 0 в соответствующую ячейку, что будет означать «место занято».
#include <iostream>
using namespace std;

int main()
{
setlocale(LC_ALL, "rus");

const int AMOUNT_FLOORS = 7; // к-во этажей
const int AMOUNT_PARKINGS = 10; // к-во парковочных мест на этаже
int floorsAndParkings[AMOUNT_FLOORS][AMOUNT_PARKINGS]; // объявление двумерного массива

// присвоение значений и отображение
cout << "~~Таблица мест паркинга (0 - место забронировано)~~" << endl << endl;

for (int f = 0; f < AMOUNT_FLOORS; f++) // используем встроенные циклы
{
cout << f + 1 << "-й этаж:  ";
for (int p = 0; p < AMOUNT_PARKINGS; p++)
{
floorsAndParkings[f][p] = p + 1; // присвоить значение
cout << floorsAndParkings[f][p] << " | "; // сразу показать
}
cout << endl << "---------------------------------------------------" << endl;
}

int floor = 0; // этаж
int parkingPlace = 0; // парковочное место
char exit = '1'; // для выхода из do while

cout << "Чтобы забронировать паркинг, выберите этаж и место.\n";

do // внешний do while
{
do // встроенный do while для выбора этажа
{
cout << "Введите номер этажа: ";
cin >> floor;

if (floor < 1 || floor > 7) // если такого этажа нет
{
cout << "Такого этажа нет!  Выберите этаж от 1 до 7!\n";
}
} while (floor < 1 || floor > 7);


do // встроенный do while для выбора места
{
cout << "Введите номер места парковки: ";
cin >> parkingPlace;

if (parkingPlace < 1 || parkingPlace > 10)
{
cout << "Такого номера нет! Выберите место от 1 до 10!\n";
}
} while (parkingPlace < 1 || parkingPlace > 10);

if (floorsAndParkings[floor - 1][parkingPlace - 1] != 0) // если место свободно
{
floorsAndParkings[floor - 1][parkingPlace - 1] = 0; //  отметить, как забронированное
cout << "\n\nБронирование прошло успешно!\n";
cout << "Ваше место парковки: " << floor << "-й этаж " << parkingPlace << "-е место!\n\n";
cout << "Забронировать еще - нажмите 1. Выйти - 0: ";
cin >> exit;
}
else // если место занято (хранит значение 0)
{
cout << "\nМесто занято! Выберите другое!\n";

// отобразить таблицу, чтобы было видно какие места свободны
cout << "~~Таблица мест паркинга (0 - место забронировано)~~" << endl << endl;

for (int f = 0; f < AMOUNT_FLOORS; f++)
{
cout << f + 1 << "-й этаж:  ";
for (int p = 0; p < AMOUNT_PARKINGS; p++)
{
cout << floorsAndParkings[f][p] << " | ";
}
cout << endl << "---------------------------------------------------" << endl;
}
}
} while (exit != '0');

return 0;
}

Мы использовали цикл for ,в строках 15 — 24, для записи данных в массив и одновременно отображения их на экране. Если представлять этот двумерный массив как таблицу — то внешний цикл for проходит по индексам строк — от 0-й до 6-й. Вложенный цикл — по индексам столбцов (по ячейкам строк таблицы) — от 0-й до 9-й. В строках 32 — 82 находится цикл do while. Его роль в том, чтобы снова и снова предлагать забронировать место для автомобиля, пока это необходимо пользователю. В нем находятся два вложенных цикла do while. Они реализовывают выбор этажа и места для парковки с защитой от некорректного ввода значений. Строки 57 — 81 содержат блок if else , который, в случае корректного выбора пользователя выводит сообщение об успешном бронировании. Если же место занято (ячейка содержит значение 0) — сообщает об этом, предлагает повторить выбор этажа и места и отображает обновлённую схему паркинга, где отмечены забронированные места.

Работает это так:

продолжение…