6

Как объявить 2D массив в C++ с использованием оператора new?

32

Как объявить двумерный массив с использованием оператора new?

Я знаю, как создать "нормальный" одномерный массив, например:

int* ary = new int[Size];

Но когда я пытаюсь сделать то же самое для двумерного массива, используя следующий код:

int** ary = new int[sizeY][sizeX];
  1. это не работает/не компилируется, и b) это не достигает той же цели, что:
int ary[sizeY][sizeX];

Как правильно создать двумерный массив с помощью оператора new?

5 ответ(ов)

3

Чтобы создать двумерный массив в C++, вы не можете использовать new int[sizeY][sizeX], поскольку это не корректная схема выделения памяти для массива указателей. Вместо этого правильный способ выглядит следующим образом:

int **ary = new int*[sizeY];
for(int i = 0; i < sizeY; ++i) {
    ary[i] = new int[sizeX];
}

После того как вы создали массив, важно не забыть освободить выделенную память. Для этого используйте следующий код:

for(int i = 0; i < sizeY; ++i) {
    delete [] ary[i];
}
delete [] ary;

ПРАВКА: Как указал Dietrich Epp в комментариях, это решение не является самым легковесным. Альтернативный подход — использовать один большой блок памяти:

int *ary = new int[sizeX * sizeY];

// доступ к элементам массива теперь будет выглядеть так:
ary[i * sizeX + j];

Этот метод будет более эффективным с точки зрения использования памяти и скоростью доступа.

1

В C++11 возможно создать двумерный массив следующим образом:

auto array = new double[M][N]; 

При этом память не будет инициализирована. Чтобы инициализировать массив, сделайте так:

auto array = new double[M][N]();

Ниже представлен пример программы (компилируйте с флагом "g++ -std=c++11"):

#include <iostream>
#include <utility>
#include <type_traits>
#include <typeinfo>
#include <cxxabi.h>
using namespace std;

int main()
{
    const auto M = 2;
    const auto N = 2;

    // выделяем память (без инициализации)
    auto array = new double[M][N];

    // заполняем память данными
    array[0][0] = 2;
    array[1][0] = 3;
    array[0][1] = 4;
    array[1][1] = 5;

    // повторно выделяем память, возможно получим тот же блок памяти (непортативно)
    delete[] array;
    array = new double[M][N];

    // показываем, что память не инициализирована
    for(int r = 0; r < M; r++)
    {
        for(int c = 0; c < N; c++)
            cout << array[r][c] << " ";
        cout << endl;
    }
    cout << endl;

    delete[] array;

    // правильный способ инициализации массива нулями
    array = new double[M][N]();

    // показываем, что память инициализирована
    for(int r = 0; r < M; r++)
    {
        for(int c = 0; c < N; c++)
            cout << array[r][c] << " ";
        cout << endl;
    }

    int info;
    cout << abi::__cxa_demangle(typeid(array).name(), 0, 0, &info) << endl;

    return 0;
}

Результат выполнения программы будет таким:

2 4 
3 5 

0 0 
0 0 
double (*) [2]

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

0

Я предполагаю, что из вашего примера со статическим массивом вы хотите создать прямоугольный массив, а не зубчатый. Вы можете использовать следующий код:

int *ary = new int[sizeX * sizeY];

После этого вы сможете обращаться к элементам массива следующим образом:

ary[y * sizeX + x]

Не забудьте освободить память, используя delete[] для ary.

0

В языке C++ двумерный массив можно рассматривать как одномерный массив указателей, где каждый указатель указывает на одномерный массив, который будет хранить фактические данные.

Здесь N — это количество строк, а M — количество столбцов.

Динамическое выделение памяти

int** ary = new int*[N];
for(int i = 0; i < N; i++)
    ary[i] = new int[M];

На этом этапе мы создаем массив из N указателей на массивы типа int, а затем для каждого указателя выделяем память для одномерного массива из M элементов типа int.

Заполнение массива

for(int i = 0; i < N; i++)
    for(int j = 0; j < M; j++)
        ary[i][j] = i;

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

Вывод массива

for(int i = 0; i < N; i++)
    for(int j = 0; j < M; j++)
        std::cout << ary[i][j] << "\n";

Эта часть кода выводит все элементы двумерного массива, построчно.

Освобождение памяти

for(int i = 0; i < N; i++)
    delete [] ary[i];
delete [] ary;

Наконец, после того как массив больше не нужен, мы освобождаем выделенную память. Сначала мы освобождаем память, выделенную для каждого одномерного массива (строк), а затем освобождаем память для самого массива указателей.

Таким образом, мы эффективно управляем памятью при работе с двумерными массивами в C++.

0

На протяжении 15 лет у меня была проблема с созданием динамического многомерного массива, и ни одно из предложенных решений меня не устраивало. Наконец, я нашел ответ. С помощью следующего кода вы сможете создать такой массив, который будет располагаться в непрерывной области памяти:

#include <iostream>

int main(int argc, char** argv)
{
    if (argc != 3)
    {
        std::cerr << "Вы должны указать размеры двух измерений массива" << std::endl;
        return -1;
    }

    int sizeX, sizeY;

    sizeX = std::stoi(argv[1]);
    sizeY = std::stoi(argv[2]);

    if (sizeX <= 0)
    {
        std::cerr << "Некорректный размер по оси x" << std::endl;
        return -1;
    }
    if (sizeY <= 0)
    {
        std::cerr << "Некорректный размер по оси y" << std::endl;
        return -1;
    }

    /******** Создание двумерного динамического массива в непрерывной памяти ******
     *
     * - Определите указатель для хранения массива
     * - Выделите память для массива (линейно)
     * - Выделите память для указателей внутри массива
     * - Присвойте указателям внутри массива соответствующие адреса
     *   в линейном массиве
     **************************************************************************/

    // Результирующий массив
    unsigned int** array2d;

    // Выделение линейной памяти
    unsigned int* temp = new unsigned int[sizeX * sizeY];

    // Эти шаги важны:
    // Выделите память для указателей внутри массива,
    // которые будут использоваться для индексации линейной памяти
    array2d = new unsigned int*[sizeY];

    // Укажите указателям внутри массива правильные адреса памяти
    for (int i = 0; i < sizeY; ++i)
    {
        array2d[i] = (temp + i * sizeX);
    }

    // Заполнение массива возрастающими числами
    for (int y = 0; y < sizeY; ++y)
    {
        for (int x = 0; x < sizeX; ++x)
        {
            array2d[y][x] = x + y * sizeX;
        }
    }

    // Код для тестирования
    // Печать адресов
    for (int y = 0; y < sizeY; ++y)
    {
        for (int x = 0; x < sizeX; ++x)
        {
            std::cout << std::hex << &(array2d[y][x]) << ' ';
        }
    }
    std::cout << "\n\n";

    // Печать массива
    for (int y = 0; y < sizeY; ++y)
    {
        std::cout << std::hex << &(array2d[y][0]) << std::dec;
        std::cout << ": ";
        for (int x = 0; x < sizeX; ++x)
        {
            std::cout << array2d[y][x] << ' ';
        }
        std::cout << std::endl;
    }

    // Освобождение памяти
    delete[] array2d[0];
    delete[] array2d;
    array2d = nullptr;

    return 0;
}

Когда вы запустите программу с параметрами sizeX=20 и sizeY=15, выход будет следующим:

... (здесь будет вывод адресов и значений) ...

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

Этот метод можно расширить на более чем два измерения, применяя ту же концепцию. Я не буду делать это здесь, но как только вы поймете суть, это станет простой задачей.

Надеюсь, этот код поможет вам так же, как он помог мне.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь