C ++ 3d array - динамическое распределение памяти выровнено в одну строку - PullRequest
4 голосов
/ 06 ноября 2011

У меня довольно странный вопрос, который, вероятно, не имеет практического смысла, но ответы меня сильно беспокоят.Сегодня я попытался немного поработать с массивами и с тем, как они распределяются в памяти, используя этот код: (компилятор Xcode 4, 4 байта)

int ***c;
int size_x = 0;
int size_y = 0;
int size_z = 0;

cout << "Enter x: " << endl;
cin >> size_x;
cout << "Enter y: " << endl;
cin >> size_y;
cout << "Enter z: " << endl;
cin >> size_z;

c = new int**[size_x];
for (int i = 0; i < size_x; ++i) {
    *(c+i) = new int*[size_y];
    for (int j = 0; j < size_y; ++j) {
        *(*(c+i)+j) = new int[size_z];
    }
}

for (int i = 0; i < size_x; ++i) {
    for (int j = 0; j < size_y; ++j) {
        for (int k = 0; k < size_z; ++k) {
            cout << (*(*(c+i)+j)+k) << endl;
            //cout << &c[i][j][k] << endl;
        }
    }
}

delete [] c;

Когда я ввожу сейчас: 3, 2 и4 я получаю следующий вывод в консоли:

1005 * 0x100100a60 0x100100a64 0x100100a68 0x100100a6c 0x100100a70 0x100100a74 0x100100a78 0x100100a7c 0x100100a90 0x100100a94 0x100100a98 0x100100a9c 0x100100aa0 0x100100aa4 0x100100aa8 0x100100aac 0x100100ac0 0x100100ac4 0x100100ac8 0x100100acc 0x100100ad0 0x100100ad4 0x100100ad8 0x100100adc 1007 * Что мой вопросТеперь, если мы посмотрим на вывод, то увидим, что в основном память выравнивается каждые 4 байта, но иногда мы видим больший шаг, например, от 0x100100a7c до 0x100100a90.

Это нормально и как я могу предотвратить это?Почему это?Есть ли возможность заставить c выровнять мою память как постоянную строку?(Я не родной английский, так что извините за это, но я не знаю, как это лучше сказать)

Это просто для общего понимания: -)

Спасибо тебе!

PS Достаточно ли использовать delete [] один раз или мне нужно пройти через каждый из 3 блоков памяти и удалить [] весь массив?РЕДАКТИРОВАТЬ:

Теперь я удаляю память, и это работает довольно хорошо:

cout << "Free Memory" << endl;

for (int i = 0; i < m_sx; ++i) {
    for (int j = 0; j < m_sy; ++j) {
        delete [] m_array[i][j];
        //delete [] (*(*(m_array)+j)+k);
    }
    delete [] m_array[i];
}

delete [] m_array, m_array = NULL;

Ответы [ 5 ]

6 голосов
/ 06 ноября 2011

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

int *A = new int[size_x * size_y * size_z];

или, что безопаснее

std::vector<int> A(size_x * size_y * size_z);

, а затем индексировать его с помощью

int element = A[i * size_z * size_y + j * size_z + k]

чтобы получить элемент на (i,j,k).

Это, на самом деле, очень полезно, так как дает вам многомерные массивы с небольшими накладными расходами, сохраняя локальность ссылок и предотвращаяКосвенная.Кроме того, обработка ошибок для этой схемы размещения намного проще, поэтому вы рискуете избежать утечек памяти.Любая хорошая матричная библиотека будет реализована таким образом.Для C ++ это включает Boost.MultiArray .

Что касается освобождения: да, вам нужно несколько вызовов на delete[] в вашей нынешней схеме.

4 голосов
/ 27 февраля 2013

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

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);

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] << " ";
        }
        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 }}
}
3 голосов
/ 06 ноября 2011

Поскольку это тег C, здесь также ответ C. Поскольку многомерные массивы C99 могут обрабатываться достаточно эффективно, даже если размеры являются динамическими:

double c[size_x][size_y][size_z];

Это распределяет матрицу непрерывно в стеке. Матричные элементы доступны для c[i][j][k], и компилятор выполняет всю арифметику индексации за вас. Если вы боитесь, что это может привести к SO, вы можете легко позвонить malloc с этим:

double (*c)[size_y][size_z] = malloc(sizeof(double[size_x][size_y][size_z]));
2 голосов
/ 06 ноября 2011

Проблема не в том, что ваша память не выровнена ... требование спецификации C ++ для вызова new и new[] заключается в том, что он передает указатель, указывающий на непрерывную память, которая правильно выровнена для платформа и размер запрашиваемого объекта.

Ваша проблема в том, что вы выделяете не весь буфер для массива одним вызовом new[], а несколькими вызовами new[]. Поэтому, хотя каждый вызов new будет возвращать выровненную и непрерывную память, множественные вызовы new[] не требуются для возврата буферов памяти, которые сами распределены непрерывно. Например, каждый вызов new[] возвращает выровненную память, но, как вы заметили, в начале каждого массива памяти могут быть «пробелы», которые new возвращает. Причиной этих «пробелов» может быть несколько причин, и они действительно зависят от того, как базовая ОС выделяет память для вашей программы.

Если вы не хотите, чтобы в каждом массиве были какие-либо «пробелы», вам нужно будет выделить весь буфер за один вызов new.

Наконец, чтобы ответить на ваш вопрос о delete[], да, поскольку вы не выделили весь буфер памяти одним вызовом new[], вы не можете удалить свой массив с помощью одного вызова delete[]. Каждый вызов new[] должен быть связан с вызовом delete[], поскольку это были отдельные выделения памяти.

1 голос
/ 06 ноября 2011

Да, это нормально. Вы распределяете данные построчно; Вы можете быть уверены только в том, что данные будут смежными в каждой строке.

...