Помимо одномерных массивов вам может понадобиться для работы использование многомерного массива (двумерного, трёхмерного…). В этом уроке будут рассмотрены двумерные массивы. Они самые распространенные, а остальные встречаются крайне редко.
Мы уже рассматривали в предыдущих статьях одномерные массивы и Си-строки (символьные массивы).Там говорилось, что элементы массива размещаются в памяти последовательно – элемент за элементом. Визуально их можно представить в виде одной строки данных в памяти. Чтобы обратиться к какому-либо элементу такого массива, достаточно указать его имя и индекс элемента. Первое отличие двумерного массива от одномерного – его элементы содержат два индекса: 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 } };
Чтобы такая инициализация выглядела более читабельно, оформим её так:
1 2 3 4 5 | int floorsAndParkings[2][4] { { 1, 2, 3, 4 }, // инициализация floorsAndParkings[0] { 1, 2, 3, 4 } // инициализация floorsAndParkings[1] }; |
Как вы помните, согласно стандарту C++11, знак = можно упустить. Строки инициализируются по тому же принципу:
1 2 3 4 5 6 | char someStr[3][16] { "Двумерные ", "массивы ", "в С++!" }; |
Как вывести на экран данные двумерного массива? Можно пойти длинным путём и обращаться к каждому элементу вручную:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #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'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #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 в соответствующую ячейку, что будет означать “место занято”.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | #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) – сообщает об этом, предлагает повторить выбор этажа и места и отображает обновлённую схему паркинга, где отмечены забронированные места.
Работает это так:
продолжение…
Предлагаю решить несколько задач по теме двумерные массивы.
Рекомендую посмотреть эти видео – Двумерные массивы
Уважаемый автор статьи и сайта, если вы одно лицо…Статья про двухмерные массивы отвратительная!!! Вместо того чтоб показать как правильно заполнить двухмерный массив – объяснить, что надо использовать вложений цикл for в такой же цикл for и как вывести двумерный массив на экран, опять же с помощью вложенных циклов, или подобные необходимые вещи!!!, вы бабахаете никому не нужный код на 80 строк!!! У вас же сайт для начинающих!!?? А после еще и задачи про это даёте…. В общем ужасно изложено!
всего-то 80 строк :)
Привет
Статья великолепная! Это просто классика. Пример зачетный. И массивы, и ввод-вывод и логика и обход массива, все в одном. Вы antidot когда дорастете, поймете о чем я.
А если у вас antidot от такого примера уже голова кипит :), это не очень хорошо.
Уважаемый автор объясните, пожалуйста, что такое “” , “\n … или где о таком можно почитать.
Это специальная символьная последовательность, которая позволяет выводить символы на экран с новой строки.
Почитать можно тут: https://purecodecpp.com/archives/302
Заметил баг, когда заходишь с вк то твоя аватарка будет на месте ВСЕХ аватарок, нетолько своих.
Я как новичок совсем не могу понять вот это: floorsAndParkings[floor – 1][parkingPlace – 1] != 0 Как массив может бить равен какому-то одному значению.
Ведь массив заполняется множеством елементов, которые записываются в фигурных скобках.Обьясните пожайлуста, сам не могу.
Сергей, floorsAndParkings[floor – 1][parkingPlace – 1] != 0 это не весь массив, а его отдельная конкретная ячейка. Например: пользователь вводит номер этажа 2 (floor ) и номер парковочного места 3 (parkingPlace). Мы помним, что нумерация ячеек массива начинается с нуля, а не с единицы – поэтому отнимаем от введенных значений по единице. Получаем конкретную ячейку floorsAndParkings[1][2] и проверяем её значение на неравенство нулю. Если не ноль -значит место свободно…
Спасибо большое, теперь я все понял☻☻☻☺☺☺
Интересно как предоставить возможность ввода элементов многомерного массива пользователю, а не в процессе написания программы?
Андрей, мы готовим сейчас задачу с решением. Там будет реализован класс Matrix. В задаче будет показано, как оформить ввод двумерного массива с клавиатуры. То есть пользователь будет вводить числа. Enter = конец строки, Enter дважды = конец ввода матрицы.
Говнокод. и помоему ещё и ложь!!!!! проведу сейчас некоторые исследования. Но предварительно я думаю в памяти не хранится двумерный массив (а указатели на начала одномерных массивов) надо проверить.
таки да, хранится в виде двумерного массива (как в статье). считаю это багом с++
Прежде, чем повторять слова типа “говнокод”, услышанные от понтующихся дебилов, которые сами вы не понимаете – вы бы прежде сами разобрались с самыми элементарными понятиями … даже не C++, а идущими от C, которые описаны 40 лет назад (это тот случай, когда говорится: читай матчасть).
Спасибо за статью!
Почему, при повторном выведений таблицы, когда место занято, во вложенном цикле for 75-78, не пишем вот эту строку(20): floorsAndParkings[f][p] = p + 1; ???