4

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;
}
Marco
  • 155
  • 5
  • Let's backup, you are not allocating arrays, you are allocating storage to pointers.? – David C. Rankin Sep 02 '19 at 05:47
  • Create a 1D array that has enough storage for the entire 3D array, then perform all of the indexing by hand to map 3 dimensions to 1. [Here is an example](https://stackoverflow.com/a/2076668/4581301) in 2D that you can extend to 3D. – user4581301 Sep 02 '19 at 05:49
  • Important question is, is the array size known at compile time, or is it for example read from file or user and can be anything? – hyde Sep 02 '19 at 05:54
  • @hyde Yes it is known at compile time. – Marco Sep 02 '19 at 06:05

3 Answers3

7

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.

6502
  • 112,025
  • 15
  • 165
  • 265
3

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
}
peterzuger
  • 106
  • 2
  • 8
2

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.

cocool97
  • 1,201
  • 1
  • 10
  • 22
  • 1
    3D array can also be 3-dimensional array with no pointers anywhere. What you describe is often called "jagged array", because different rows can be of different size. – hyde Sep 02 '19 at 05:53
  • That's true yes ! But the user didn't asked especially for a "classicé array. He could allocate the index members with a for loop, and just use a constant as size for each member. – cocool97 Sep 02 '19 at 05:56
  • 1
    Question asks for "continuous memory". – hyde Sep 02 '19 at 05:58
  • @hyde Continous means that take only one space range in memory, not that all colums has to do the same size isn't it ? – cocool97 Sep 02 '19 at 06:00
  • 1
    @cocool97 if you make pointers and then allocate memory for them the memory for the whole thing will not be contiguous - especially if the array is bigger than one virtual page. – Jerry Jeremiah Sep 02 '19 at 06:10
  • I have used an array of pointers to rows in 2D matrixes but the actual data was one contiguous block. This approach was reasonably efficient with old, cheap, embedded system CPUs where multiplies were slower than ptr lookup. Those days are long over. – doug Sep 02 '19 at 06:46