5

I have to assign memory to a 3D array using a triple pointer.

#include <stdio.h>
int main()
{
    int m=10,n=20,p=30;
    char ***z;
    z = (char***) malloc(sizeof(char**)*m*n*p);
    return 0;
}

Is this correct way of doing this?(I think what i am doing is incorrect.)

Rog Matthews
  • 3,147
  • 16
  • 37
  • 56
  • It's been a long time since I did C, but on the surface this looks correct. What do you think is wrong? – Eric J. May 28 '12 at 09:02
  • 1
    in C, do not cast the return value of malloc, especially if you do not include stdlib.h since functions by default get return value int if there is no prototype. – AndersK May 28 '12 at 09:09
  • @AndersK Can you please explain – Rog Matthews May 28 '12 at 09:12
  • 1
    See http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc for not casting return value of `malloc()`. – hmjd May 28 '12 at 09:13
  • Many duplicates, e.g. [Malloc a 3-Dimensional array in C?](http://stackoverflow.com/questions/2306172/malloc-a-3-dimensional-array-in-c), [Dynamic memory allocation for 3D array](http://stackoverflow.com/questions/2438142/dynamic-memory-allocation-for-3d-array) and [dynamic allocation/deallocation of 2D & 3D arrays](http://stackoverflow.com/questions/1824363/dynamic-allocation-deallocation-of-2d-3d-arrays). – Paul R May 28 '12 at 09:44

5 Answers5

6

To completely allocate a 3D dynamic array you need to do something like the following:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int m=10,n=20,p=30;
    char ***z;

    z = malloc(m * sizeof(char **));
    assert(z != NULL);
    for (i = 0; i < m; ++i)
    {
        z[i] = malloc(n * sizeof(char *));
        assert(z[i] != NULL);
        for (j = 0; j < n; ++j)
        {
            z[i][j] = malloc(p);
            assert(z[i][j] != NULL);
        }
    }
    return 0;
}

Freeing the data is left as an exercise for the reader.

Paul R
  • 208,748
  • 37
  • 389
  • 560
  • strictly speaking its not really appropriate to use asserts to handle runtime errors. – AndersK May 28 '12 at 10:34
  • @Anders K: true - I just wanted to highlight the fact that malloc may fail, and that this should be handled in some way, without adding so much error handling code that it obscured the rest of the code. I'll see I can find a better way... – Paul R May 28 '12 at 10:50
  • 1
    Very nice explanation of allocating memory for 3D array using pointer. – aliceangel Apr 22 '18 at 08:21
4

There's no need to cast the return value of malloc(), in C.

And if you expect to store m * n * p characters directly (and compute the address yourself), then you should of course not scale the allocation by the size of a char **.

You mean:

int m = 10, n = 20, p = 30;
char *z = malloc(m * n * p * sizeof *z);

This will allocate 10 * 20 * 30 = 6000 bytes. This can be viewed as forming a cube of height p, with each "slice" along the vertical axis being n * m bytes.

Since this is for manual addressing, you cannot use e.g. z[k][j][i] to index, instead you must use z[k * n * m + j * m + i].

unwind
  • 391,730
  • 64
  • 469
  • 606
2

If you don't need the memory to be allocated in a single, contiguous chunk (which IME is the usual case), you would do something like this:

char ***z;
z = malloc(sizeof *z * m); // allocate m elements of char **
if (z)
{
  int i;
  for (i = 0; i < m; i++)
  {
    z[i] = malloc(sizeof *z[i] * n); // for each z[i], 
    if (z[i])                        // allocate n elements char *
    {
      int j;
      for (j = 0; j < n;j++)
      {
        z[i][j] = malloc(sizeof *z[i][j] * p); // for each z[i][j], 
        if (z[i][j])                           // allocate p elements of char
        {
           // initialize each of z[i][j][k]
        }
      }
    }
  }
}

Note that you will need to free this memory in reverse order:

for (i = 0; i < m; i++)
{
  for (j = 0; j < n; j++)
    free(z[i][j];
  free(z[i]);
}
free(z);

If you really need the memory to be allocated in a contiguous chunk, you have a couple of choices. You could allocate a single block and compute your offsets manually:

char *z = malloc(sizeof *z * m * n * p); // note type of z!
...
z[i * m + j * n + k] = some_value();

When you're done, you just need to do a single free:

free(z);

If you have a C99 compiler or a C11 compiler that supports variable-length arrays, you could do something like this:

int m=..., n=..., p=...;
char (*z)[n][p] = malloc(sizeof *z * m);

This declares z as a pointer to an nxp array of char, and we allocate m such elements. The memory is allocated contiguously and you can use normal 3-d array indexing syntax (z[i][j][k]). Like the above method, you only need a single free call:

free(z);

If you don't have a C99 compiler or a C11 compiler that supports VLAs, you would need to make n, and p compile-time constants, such as

#define n 20
#define p 30

otherwise that last method won't work.

Edit

m doesn't need to be a compile-time constant in this case, just n and p.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • A `const`-qualified object in C is *not* a compile-time constant, which C89 requires for an array dimension. C++ is different in that regard. – John Bode May 29 '12 at 11:14
0

You would need the following nested loop -

z = (char**)malloc(sizeof(char*) * m);
for (int i = 0; i < m; ++i)
{
    *(z + i) = (char*)malloc(sizeof(char*) * n);
    for (int j = 0; j < n; ++j)
    {
        *(*(z + i)) = (char)malloc(p);
    }
}

May not be synactically accurate, but it should be something along these lines.

Superman
  • 3,027
  • 1
  • 15
  • 10
0

You want sizeof(char) not sizeof(char**) as the latter will give you the size of a pointer which on most modern systems will be 4 bytes instead of the 1 you're expecting.

Jon Cage
  • 36,366
  • 38
  • 137
  • 215