Search code examples
c++dynamic-memory-allocation

Why does the top code work and the bottom code not in c++ for dynamic matrix allocation?


I've gotten rid of the codes that do work as they were distracting. What I'm trying to do is allocate a single block of memory for a 3D array. I could simply just do the allocation as a jagged array, but it's more efficient to do a contiguous block.

Here's my current code snippet:

//Creating an array of proper size
//I first create an array of pointers
phi = new double**[xlength];
//I then create another array of pointers
phi[0] = new double*[xlength*ylength];
//At this point, I assume phi[0] and phi[0][0] have the same location. Is this not true?
phi[0][0] = new double[xlength*ylength*tlength];
//Now, I allocate the big block of data that will actually store my data
//I assume phi[0][0][0] has the same location as phi[0][0]. Is this right?
//Now, I'm trying to setup the pointers to match up in the single block of memory
    for (int i=0;i<xlength;i++)
    {
        for (int j=0;j<ylength;j++)
        {
            phi[i][j] = phi[0][0] + tlength*ylength*i + tlength*j;
        }
    }
//Does this not work? It feels as though it SHOULD

Adding back working code since answer depends on working code

double*** A = new double**[m];
double**  B = new double*[m*n];
double*   C = new double[m*n*o];
for (int i=0; i<m; i++) {
    for (int j=0; j<n; j++) {
        B[n*i+j] = C + (n*i+j)*o;
    }
    A[i] = B + n*i;
}
for (int i=0; i<m; i++) {
    for (int j=0; j<n; j++) {
        for (int k=0; k<o; k++) {
            A[i][j][k] = <my_value>;
        }
    }

Here's the solution guys!

Don't do 3D matrix, LOL! This is my most unpopular post ever!

Do a 1D matrix and follow the answer on this link C++ Segmentation Fault After When Trying to Write to Matrix

This is the important part of that link:

but then you cannot use the phi[i][j][k] notation, so you should

#define inphi(I,J,K) phi[(I)*xlength*ylength+(J)*xlength+(K)]

and write inphi(i,j,k) instead of phi[i][j][k]


Solution

  • Remarks:

    • You do not free allocated memory by using delete[]. That's a very bad practice. You always should remember to free memory you allocated.

    • It is very, very uncomfortable to choose jagged arrays (arrays of arrays) for long term use. A lot easier solution is to allocate a single array:

      double * phi = new double[xlength * ylength];
      

      And then access (x, y)-th element as following:

      phy[y * xlength + x] = 20.0;
      

      The allocation takes less time, you have a lot less things to free (only the phi itself) and access time is more-less equally fast.

    • Consider using std::vector or std::array. Since you use C++ and not C, the natural way is to use these containers instead of raw arrays, they are a lot more managable and they care themselves to free their contents if allocated statically. In your case it would look like:

      #include <vector>
      
      (...)
      
      std::vector<std::vector<double> > phi;
      
      phi.resize(xlength);
      for (int x = 0; x < xlength; x++)
          phi[x].resize(ylength);
      

    Solution:

    Your solution won't work. The reason, why author of the original code uses three variables is, that one of them has to contain the actual data and two others serve as pointers to parts of the original data.

    In your case, you try to keep both the data and pointers to parts of the data in the same array, what simply cannot work. If you want the [][][] notation, you have to allocate jagged arrays in nested loops, just as I've shown in my solution. On the other hand, if you want one array to keep single block of data and another to keep pointers, you'll have to do it as author of the first piece of code did.


    It took me a few minutes of time to figure out, how the three-dimensional solution with three variables actually work, so I'll leave an explanation for everyone, who'll encounter this thread.

    The general idea is to have one variable with actual data and two proxy variables with a set of pointers, which allows addressing the actual data with [][][] notation.

    C contains the actual data, so it is of size [zDim * yDim * xDim]. You can access (x, y, z) element by addressing it by [z * xDim * yDim + y * xDim + x]. [][][] notation means, that the data is organized by slices (z), which contains rows (y), which contains elements(x).

    You construct array B containing pointers to all rows, ordered by slice. So B contains: (slice 0, row 0), (slice 0, row 1), ..., (slice 0, row yDim - 1), (slice 1, row 0), ...

    Then, you construct array A containing pointers to elements of array B, such that z-th element of A points to (z * yDim) = 0-th row of z-th slice. Finally, when you address the array A, it works like following:

    • A[z] contains address to element of array B containing pointer to 0th row of zth slice
    • A[z][y] moves this pointer, such that now we have actual pointer to 0-th element of y-th row of z-th slice.
    • Finally, A[z][y][x] shifts us by x elements, such that we receive x-th element of y-th row of z-th slice.

    Now it should be clear, why you need additional variables and why it cannot be done with only single variable.

    Bottomline: never use such solution, its a huge waste of memory. Instead, flatten your arrays and address by [z * xDim * yDim + y * xDim + x] or use jagged std::vectors or std::arrays.