|
|||||||
Многомерные массивы C++ в динамической памяти через new и delete
Время создания: 05.12.2023 16:06
Текстовые метки: C++, Си++, массив, многомерный, двумерный, двухмерный, трехмерный, new, delete, куча, динамическая, память
Раздел: Компьютер - Программирование - Язык C++ (Си++)
Запись: xintrea/mytetra_syncro/master/base/17017815980kfztvsc3m/text.html на raw.github.com
|
|||||||
|
|||||||
Создание многомерных массивов в динамической памяти (куче) через ключевое слово new, можно рассмотреть на примере двухмерных массивов. Работа с массивом в старом Си-стиле Что такое, по сути, двухмерный массив? Это массив массивов. Соответственно, чтобы создать двухмерный массив в динамической памяти, нам надо создать общий динамический массив указателей (это первое измерение), а затем создать его элементы, которые будут вложенными массивами, так же размещенными в динамической памяти (второе измерение). В общем случае это выглядит так: #include <iostream>
int main() { unsigned rows = 3; // Количество строк unsigned columns = 2; // Количество столбцов // Выделяется память под первое измерение int** numbers{new int*[rows]{}}; // Выделяется память для вложенных массивов (второе измерение) for (unsigned i{}; i < rows; i++) { numbers[i] = new int[columns]{}; } ... // Удаление массивов второго измерения for (unsigned i{}; i < rows; i++) { delete[] numbers[i]; } // Удаление массива первого измерения delete[] numbers; } Здесь вначале выделяется память для массива указателей (первое измерение, условно столбцы): int** numbers{new int*[rows]{}}; Затем в цикле выделяем память для каждого отдельного массива (условно строки массива): numbers[i] = new int[columns]{}; Освобождение памяти идет в обратном порядке - сначала освобождается память для каждого отдельного вложенного массива, а затем для всего массива указателей. Пример с вводом и выводом данных двухмерного массива в динамической памяти: #include <iostream>
int main() { unsigned rows = 3; // Количество строк unsigned columns = 2; // Количество столбцов int** numbers{new int*[rows]{}}; // Память для первого измерения for (unsigned i{}; i < rows; i++) { numbers[i] = new int[columns]{}; // Память для второго измерения } // Ввод данных для таблицы rows x columns for (unsigned i{}; i < rows; i++) { std::cout << "Enter data for " << (i + 1) << " row" << std::endl; // Ввод данных для столбцов i-й строки for (unsigned j{}; j < columns; j++) { std::cout << (j + 1) << " column: "; std::cin >> numbers[i][j]; } } // Перебор строк for (unsigned i{}; i < rows; i++) { // Вывод данных для столбцов i-й строки for (unsigned j{}; j < columns; j++) { std::cout << numbers[i][j] << "\t"; } std::cout << std::endl; } for (unsigned i{}; i < rows; i++) { delete[] numbers[i]; } delete[] numbers; } Пример работы программы: Enter data for 1 row 1 column: 2 2 column: 3 Enter data for 2 row 1 column: 4 2 column: 5 Enter data for 3 row 1 column: 6 2 column: 7 2 3 4 5 6 7 Указатель на массив Помимо типа int**, который представляет указатель на указатель (pointer-to-pointer), можно использовать тип указателя на массив int (*a)[2] (pointer to array). Тип int** - это старый стиль работы с массивами, существующий даже в языке Си. При его использовании надо выделять память как для первого измерения, так и для элементов второго измерения. Только элементы "последнего" измерения хранят значения, а все предыдущие измерения являются указателями на массив следующего измерения. Это очень сложная структура, и к тому же работа с ней на уровне машинных кодов весьма неэффективна. Единственное достоинство такой структуры - это фрагментарное размещение элементов (подмассивов) в памяти, благодаря чему нет необходимости выделения одного большого непрерывного куска памяти. Это может иметь значение в системах с сильно ограниченным объемом ОЗУ. На этом достоинства заканчиваются. В языке Cи++ можно выделить непрерывный кусок памяти под многомерный массив, в котором элементы будут располагаться "вплотную" друг к другу. Размер такого массива будет четко равен количеству хранимых элементов. Никаких указателей внутри массива нет. Для такого выделения памяти для массива из int значений размера n x m используется команда: new int[n][m] Вот так просто и красиво. Но язык Си++ не был бы Си++, если б синтаксис не имел ограничения и не использовался абсолютно сумасшедший тип для работы с этим простым и понятным массивом. Ограничение: переменный размер может иметь только первое измерение. Размеры остальных измерений должны быть константными! То есть, допустимы только следующие конструкции: // Запрос заранее неизвестного размера первого измерения size_t n; std::cout << "Enter first array dimension:"; std::cin >> n;
int (*a)[10] = new int[n][10]; float (*b)[20][30] = new float[n][20][30];
Чтобы работать с "планарным" двумерным массивом размером n x m, необходим указатель a следующего вида: int (*a)[m] Где m - это константа. Соответсвенно, тип у указателя будет: int (*)[m] Для работы с трехмерными массивами, потребуется, по аналогии, тип int(*)[m][k], где m и k - константы. Да, в этом типе не видно размера n, не видно в явном виде количества измерений (чтобы пролучить количество измерений, надо взять количество квадратных скобок и увеличить на 1). Но ничего не поделаешь, это C++. Зато доступ к элементам происходит просто: a[i][j]. Чтобы продемонстрировать работу через вышеозначенный тип, можно рассмотреть следующий пример: #include <iostream>
int main() { unsigned n{3}; // Количество строк // Выделяется память и создается указатель на двумерный массив int (*a)[2] = new int[n][2]; int k{}; // Установка значений for (unsigned i{}; i < n; i++) { // Установка данных для столбцов i-й строки for (unsigned j{}; j < 2; j++) { a[i][j] = ++k; } }
// Вывод данных for (unsigned i{}; i < n; i++) { // выводим данные столбцов i-й строки for (unsigned j{}; j < 2; j++) { std::cout << a[i][j] << "\t"; } std::cout << std::endl; }
// удаляем данные delete[] a; a = nullptr; } Здесь запись int (*a)[2] представляет указатель на массив из двух элементов типа int. Так фактически можно работать с этим объектом как с двухмерным массивом (таблицей), только количество столбцов в данном случае фиксировано - 2. И память для такого массива была выделена одной командой один раз: int (*a)[2] = new int[n][2]; ... a[0][1] = 100; // Установка (т. е. запись) значения ячейки массива cout << a[0][1] << endl; // Чтение значения ячейки массива То есть в данном случае мы имеем дело с таблицей из n строк и 2 столбцов. Используя два индекса (для строки и столбца), можно обращаться к определенному элементу, установить или получить его значение. Консольный вывод данной программы: 1 2 3 4 5 6 |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|