Многомерные массивы C++ в куче

Как я могу динамически выделять многомерный массив?

Ааа, это хороший вопрос.

Paul Nathan 04.12.2008 19:03
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
23
1
25 959
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Вот реализация, которую я получил; Я объявляю один непрерывный блок int вместо создания новых блоков внутри цикла for, поэтому я не вызываю сбои страниц повсюду. Спасибо eJames за указание на то, почему изначально этот код был взломан.

int width = 10, height = 10, totalSize = width*height;
int **myArray = new int*[width];
int *data = new int[totalSize];

for ( int i = 0; i < height; ++i )
{
    myArray[i] = data + (i*width);
}

// do some things here

delete[] data;
delete[] myArray;

Этот код не будет работать, как показано. В частности, записи в myArray [i] в ​​вашем цикле будут повсюду. Смотрите мой измененный цикл здесь: stackoverflow.com/questions/340943/…

e.James 04.12.2008 19:45
Ответ принят как подходящий

Если вы уже знаете размер вложенных измерений, вы также можете буквально выделить многомерный массив, используя new:

typedef int dimensions[3][4];

dimensions * dim = new dimensions[10];
dim[/* from 0 to 9 */][/* from 0 to 2 */][/* from 0 to 3 */] = 42;
delete [] dim;

вместо 10 может быть передано значение, определенное во время выполнения. Это разрешено, поскольку оно не является частью возвращаемого оператора типа new. Это удобно, если вы знаете количество столбцов, но хотите, например, сохранить переменную количества строк. Typedef упрощает чтение кода.

это неприятный ответ: stackoverflow.com/questions/198051/…, но, надеюсь, он отвечает на ваши опасения :)

Johannes Schaub - litb 04.12.2008 18:58

Хорошенькая, литб. Я понятия не имел, что вы можете это сделать.

e.James 04.12.2008 19:42

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

int width = 10;
int height = 10;
int ** myArray = new int*[width];
int * data = new int[width*height];
int * index = data;
for (int i = 0; i < width; i++)
{
    myArray[i] = index;
    index += height;
}

// ...

delete[] data;
delete[] myArray;

Ты прав; У меня это работало, и я реорганизовал его, не проверяя, работает ли оно. Может, стоит перестать ломать сборку ...

eplawless 04.12.2008 19:54

Посмотри это: C++ FAQ от Маршалла Клайна

См. «Как разместить многомерные массивы с помощью new?» и "Но код из предыдущего FAQ ТАКОЕ сложен и подвержен ошибкам! Нет ли более простого способа?" разделы.

Я давно не думал о C++ FAQ. Наряду с книгой Строструпа, это было одно из моих любимых чтений по программированию.

Rich 04.12.2008 20:22

FAQ [16.16] не кажется правильным. Он выделяет память для строк с помощью new []. Затем устанавливает каждый указатель в NULL и перераспределяет его. Он никогда не освобождает память, для которой установлено значение NULL, что приводит к утечке этой памяти. Пожалуйста, проверьте.

user236215 29.12.2009 20:17

Следует упомянуть std::vector<std::vector<int> >, часто это самый простой способ. Однако имейте в виду, что он не прямоугольный. Не все std::vector<int> должны иметь одинаковую длину.

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

Mark Ransom 04.12.2008 20:08

Верно, но это не сложно: std :: vector <std :: vector <int>> myarray (height, std :: vector <int> (width)); создает прямоугольник, полный нулей, индексированный [строка] [столбец]. Строки в памяти непрерывны, столбцы - нет.

Steve Jessop 04.12.2008 23:26

@MarkRansom, на самом деле, для меня это преимущество этого метода. boost::multi_array - отличный контейнер, когда длины размеров согласованы, в то время как vector<vector<...>> - это способ пойти для динамических массивов некостентной длины ... который, по общему признанию, почти никогда не нужен ... но когда это так, это путь идти!

Elliott 24.09.2020 12:18

Для полноты, вот лучший способ сделать это на C++, когда вы заранее знаете границы массива. Преимущество использования следующего класса заключается в том, что вам не нужно беспокоиться о вызове delete [] для ваших данных. Это означает, что этот класс будет безопасным для исключений, как и все другие замечательные вещи о RAII.

template<typename T, int width, int height>
class MultiArray
{
    private:
        typedef T cols[height];
        cols * data;
    public:
        T& operator() (int x, int y) { return data[x][y]; }
        MultiArray() { data = new cols[width]; }
        ~MultiArray() { delete [] data; }
};

Использование:

MultiArray<int, 10, 10> myArray;
myArray(2, 3) = 4;
cout << myArray(2, 3);

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

template<typename T>
class Array2D
{
    private:
        const int width;
        T * data;
    public:
        T& operator() (int x, int y) { return data[y*width + x]; }
        Array2D(const int w, const int h) : width(w) { data = new T[w*h]; }
        ~Array2D() { delete [] data; }
};

Использование:

Array2D myArray(10, 10);
myArray(3, 4) = 42;
cout << myArray(3, 4);

Вы можете проиндексировать одномерное как 2-, 3- или N-мерное, если вы просто разместите нужное количество элементов. Например, если у меня есть 10 строк и 10 столбцов, я знаю, что если я нахожусь в строке 3, мне придется пройти не менее 30 элементов, чтобы добраться до нее.

Почему-то я предпочитаю эту нотацию для простых 2D-массивов, поскольку мне не нужно беспокоиться о вложенных уровнях указателей. Обратной стороной является более беспорядочная нотация индекса. Вот пример с 2D-массивом с n строками и m столбцами:

int *matrix = new int[n*m];

//set element (3,7) to 10
matrix[3*m+7] = 10;

//print the matrix
for (int i = 0; i < n; i++) {
  for (int j = 0; j < m; j++) {
    cout << matrix[i*m+j] << ' ';
  }
  cout << '\n';
}

Я удивлен, что еще никто не упомянул boost::multi_array. На прошлой неделе мне понадобился 2D-массив в программе, и я обнаружил, что его намного проще и быстрее кодировать, чем самодельные решения, которые я придумал раньше (все из которых упоминаются в других комментариях) .

Как насчет использования Boost.Multiarray? Я считаю, что это хорошо отвечает вашим потребностям! http://www.boost.org/doc/libs/1_37_0/libs/multi_array/doc/user.html#sec_introduction

Вот выдержка со страницы документации:

 #include < boost/multi_array.hpp >

 #include < cassert >

int main () 

{

  // Create a 3D array that is 3 x 4 x 2

  typedef boost::multi_array< double, 3 > array_type;

  typedef array_type::index index;

  array_type A(boost::extents[3][4][2]);


  // Assign values to the elements

  int values = 0;

  for(index i = 0; i != 3; ++i) 

    for(index j = 0; j != 4; ++j)

      for(index k = 0; k != 2; ++k)

        A[i][j][k] = values++;

  // Verify values

  int verify = 0;

  for(index i = 0; i != 3; ++i) 

    for(index j = 0; j != 4; ++j)

      for(index k = 0; k != 2; ++k)

        assert(A[i][j][k] == verify++);

  return 0;

}

В качестве другой альтернативы STLSoft включает в себя класс fixed_array_2d (а также версии 3D и 4D). По сравнению с приведенными здесь домашними решениями, он имеет аналогичную реализацию, но более полный набор функций (полная поддержка итераторов и т. д.). По сравнению с boost :: multi_array, он легче и проще для не совсем совместимых компиляторов C++, но (намеренно) лишен некоторых функций multi_array.

Это воспроизведение сообщения в другой ветке. Он делает именно то, что вы хотите, без необходимости заранее знать размеры массива и без использования ускорения или STL.

Это процедура, которая выделяет трехмерный массив размерности N1 x N2 x N3 в непрерывном пространстве памяти, позволяя вам использовать синтаксис a [i] [j] [k] для доступа оператора. Массив является динамическим, но непрерывным, так что это огромный плюс по сравнению с подходом vector <> и циклами вызовов new [].

template <class T> T ***Create3D(int N1, int N2, int N3)
{
    T *** array = new T ** [N1];

    array[0] = new T * [N1*N2];

    array[0][0] = new T [N1*N2*N3];

    int i,j,k;

    for( i = 0; i < N1; i++) {

        if (i < N1 -1 ) {

            array[0][(i+1)*N2] = &(array[0][0][(i+1)*N3*N2]);

            array[i+1] = &(array[0][(i+1)*N2]);

        }

        for( j = 0; j < N2; j++) {     
            if (j > 0) array[i][j] = array[i][j-1] + N3;
        }

    }

    cout << endl;
    return array;
};

template <class T> void Delete3D(T ***array) {
    delete[] array[0][0]; 
    delete[] array[0];
    delete[] array;
};

И позже в вашей рутине реализации ...

int *** array3d;
int N1=4, N2=3, N3=2;

int elementNumber = 0;

array3d = Create3D<int>(N1,N2,N3);

//equivalently, a 'flat' array could be obtained with
//int * array = array3d[0][0];

cout << "{" << endl;
for (i=0; i<N1; i++) {
    cout << "{";
    for (j=0; j<N2; j++) {
        cout << "{";
        for (k=0; k<N3; k++) {
            array3d[i][j][k] = elementNumber++;
            cout << setw(4) << array3d[i][j][k] << " ";

            //or if you're using the flat array:
            //array[i*N2*N3 + j*N3 + k] = elementNumber++;

        }
        cout << "}";
    }
    cout << "}";
    cout << endl ;
}
cout << "}" << endl;

Delete3D(array3d);

Дает результат:

{
{{   0    1 }{   2    3 }{   4    5 }}
{{   6    7 }{   8    9 }{  10   11 }}
{{  12   13 }{  14   15 }{  16   17 }}
{{  18   19 }{  20   21 }{  22   23 }}
}

Другие вопросы по теме