简体   繁体   中英

Allocate contiguous memory for a 3D array in C++

I'm able to allocate contiguous memory to allocate a 2D array in C++. I do not know how to do for a 3D array. I have already read some posts but I haven't been able to come up with a solution.

#include <iostream>


using namespace std;

int main(int argc, char **argv){
    cout << "Ints have size " << sizeof(int) << endl;

int rows= 2;
int cols= 3;

int **i= new int*[rows];
int size= rows*cols;
i[0]= new int[size];
for(int j= 1; j < rows; j++) {
  i[j]= &i[0][j*cols];
}

for(int j= 0; j < rows; j++) {
  for(int k= 0; k < cols; k++) {
    cout << j << " " << k << " " << &i[j][k] << endl;
  }
}
delete[] i;
return 0;
}

A 3d array with P planes, each of which has R rows and C columns will require P*R*C elements. You can just allocate them at once for example with:

Element *p = new Element[P*R*C];

then to access the element at coordinates (p, r, c) you can use as formula:

int index = (p*R + r)*C + c;

To make things readable an easy solution is to make a class

template<typename T>
struct Matrix3D {
    int P, R, C;
    std::vector<T> elements;

    Matrix3D(int P, int R, int C)
        : P(P), R(R), C(C), elements(P*R*C)
    { }

    T& operator()(int p, int r, int c) {
        return elements[(p*R + r)*C + c];
    }
};

in this example I'm using an std::vector to store the elements because this makes things simpler about ownership/copying and still guarantees all elements will be contiguous in memory. If you want to manually allocate the storage then more code is needed.

If the size is known at compile time then you can make P , R and C template parameters and use an std::array member instead of std::vector . This should give some performance improvement as the whole class will end up being a single chunk of memory in the heap and allowing constant multiplication tricks for element access.

An array of array (3D array) is nothing more than an array which contains a reference on an other array in each index.
You'll just have to allocate your first 2D array , and then, for each index of this array, allocate an other array inside it.

If the Space you have to allocate must be contiguous it has to be allocated with a single 'new' otherwise the memory will not be contiguous.

This would look like this:

int d1 = 10; // first
int d2 = 10; // second
int d3 = 10; // third dimension

int* array3D = new int[d1 * d2 * d3];

with this you have allocated enoug space for your 3D array, now this has to be mapped to 3D.

array3D[(1*d1*d2) + (2*d2) + (3)]; // access element at 1,2,3

With this you can Map every point of this 1D array that you Allocated onto a unique point in 3D space.

As you might see this is very error prone. So you should not ever do this like that.

Dont ever use new/delete to allocate an array like this:

use std:array or std::vector to handle this for you. The use of raw new/delete leads to errors, if anything was allocated with new and you forget to delete it, or you overlook something, there will be a memory leak.

void test(){
    int* a = new int[20];
    // do stuff with a...
    if(error)
        return; // oops this is a leak

    delete a; // only executed if there was no error,
}

std::array is to be used if you know how big the array has to be at compile time, and it never has to change.

std::vector on the other hand can be used if you dont know the size at compile time, it can change while your programm is running.

std::array<int, 10> test1; // creates a fixed size array of size 10 and type int.
std::vector<int>    test2(10); // creates an array that can change at runtime:
test2.push_back(2);            // the vector now has size 11 and the last element is equal to 2

This way you also don't have to delete the array at the end.

If you want to be able to use this more often in your code, it can be very helpful to wrap all this functionality in a class:

#include <array>

template<typename T, std::size_t _D1, std::size_t _D2, std::size_t _D3>
class Array3D{
    std::array<T, _D1*_D2*_D3> elements;
public:
    std::size_t D1(){ return _D1; }
    std::size_t D2(){ return _D1; }
    std::size_t D3(){ return _D1; }

    T& element(std::size_t d1, std::size_t d2, std::size_t d3){
        return elements[(d1*_D1*_D2) + (d2*_D2) + (d3)];
    }
};

int main(){ // argc/argv not required if you dont use them
    Array3D<int, 10, 10, 10> array;
    array.element(1,2,3) = 5;

    // loop thorug all elements
    // the methods d1,d2,d3 return the dimensions you gave them initialy
    // this way if you cange the array size you dont have to change this loop at all
    for(std::size_t i = 0; i < array.D1(); i++)
        for(std::size_t j = 0; j < array.D2(); j++)
            for(std::size_t k = 0; k < array.D3(); k++)
                array.element(i,j,k) = 5;

    // no delete
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM