12

I know about algorithms to allocate/deallocate a 2D array dynamically, however I'm not too sure about the same for 3D arrays.
Using this knowledge and a bit of symmetry, I came up with the following code.
(I had a hard time visualizing in 3D during coding).

Please comment on the correctness and suggest any better alternative (efficiency-wise or intuitively), if any.
Also, I think both these 2D and 3D arrays can be accessed normally like static arrays like arr2D[2][3] and
arr3D[2][3][2]. Right?

Code for 2D

//allocate a 2D array
int** allocate2D(int rows,int cols)
{
    int **arr2D;
    int i;

    arr2D = (int**)malloc(rows*sizeof(int*));
    for(i=0;i<rows;i++)
    {
        arr2D[i] = (int*)malloc(cols*sizeof(int));
    }
}

//deallocate a 2D array
void deallocate2D(int** arr2D,int rows)
{
    int i;

    for(i=0;i<rows;i++)
    {
        free(arr2D[i]);
    }

    free(arr2D);
}  

Code for 3D

//allocate a 3D array
int*** allocate3D(int l,int m,int n)
{
int ***arr3D;
int i,j,k;

arr3D = (int***)malloc(l * sizeof(int **));

for(i=0;i<l;i++)
{
    arr3D[i] = (int**)malloc(m * sizeof(int*));
    for(j=0;j<m;j++)
    {
        arr3D[i][j] = (int*)malloc(n*sizeof(int));
    }
}

return arr3D;
}

//deallocate a 3D array
void deallocate3D(int arr3D,int l,int m)
{
    int i,j;

    for(i=0;i<l;i++)
    {
        for(int j=0;j<m;j++)
        {
            free(arr3D[i][j]);
        }
        free(arr3D[i]);
    }
    free(arr3D);
}
Ankur
  • 11,239
  • 22
  • 63
  • 66

4 Answers4

11

You can also allocate one array and compute individual indices. This requires fewer allocator calls and results in both less fragmentation and better cache use.

typedef struct {
  int a;
  int b;
  int* data;
} Int2d;

Int2d arr2d = { 2, 3 };
arr2d.data = malloc(arr2d.a * arr2d.b * sizeof *arr2d.data);

Now arr2d[r][c] becomes arr2d.data[r * arr2d.b + c]. Deallocation is a single free() away. As a bonus you're sure to always keep your dynamic array sizes with you.

Extrapolating to 3d:

typedef struct {
  int a;
  int b;
  int c;
  int* data;
} Int3d;

Int3d arr3d = { 2, 3, 4 };
arr3d.data = malloc(arr3d.a * arr3d.b * arr3d.c * sizeof *arr3d.data);

//arr3d[r][c][d]
// becomes:
arr3d.data[r * (arr3d.b * arr3d.c) + c * arr3d.c + d];

You should encapsulate these index operations (and the (de-)allocations for that matter) in a separate function or macro.

(The names for r, c, and d could be better—I was going for row, column, and depth. While a, b, and c are the limits of their corresponding dimensions, you might prefer something like n1, n2, n3 there, or even use an array for them.)

  • you can also allocate an n dimensional array in a single block big enough to contain both pointers and data. That way you can go int ***** array = allocate (sizeof(int), 10, 10, 10, 10, 10, 0); to allocate a 5D int array, and index it by array [a][b][c][d][e] without the need to calculate the indices. I used this when I needed to replace large stack arrays with heap ones to code to work on phones with limited stack size without needing to do serious adjustment to the code indexing the arrays. See here: https://sourceforge.net/p/gnugos60/code/HEAD/tree/trunk/GNUGoS60/common/src/ndMalloc.cpp – idij Aug 03 '16 at 10:49
  • The only problem with doing multiplication seems to be a potential for overflow. Just like there is a reallocarray in BSD unixes as a replacement for realloc function. – codepoet Nov 27 '21 at 02:13
4

arr3d should be a triple pointer and not just an int. Otherwise looks OK:

void deallocate3D(int*** arr3D,int l,int m)
{
    int i,j;

    for(i=0;i<l;i++)
    {
        for(int j=0;j<m;j++)
        {
                free(arr3D[i][j]);
        }
        free(arr3D[i]);
    }
    free(arr3D);
}

arr3D is a pointer-to-pointer-to-pointer, so arr3D[i] is a pointer-to-pointer and arr3D[i][j] just a pointer. It's correct to release the lowest dimension in a loop first, and then climb up the dimensions until arr3D itself is released.

Also it's more idiomatic to give malloc the sizeof of the pointed type implicitly. Instead of:

  arr3D[i] = (int**)malloc(m * sizeof(int*));

Make it:

  arr3D[i] = (int**)malloc(m * sizeof(*arr3D[i]));

And yes, such dynamically allocated multi-dimensional arrays can be accessed just as statically allocated multi-dimensional arrays.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
1

You can see the below code:

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

void main()
{
    //  Array 3 Dimensions
    int x = 4, y = 5, z = 6;

    //  Array Iterators
    int i, j, k;

    //  Allocate 3D Array
    int *allElements = malloc(x * y * z * sizeof(int));
    int ***array3D = malloc(x * sizeof(int **));

    for(i = 0; i < x; i++)
    {
        array3D[i] = malloc(y * sizeof(int *));

        for(j = 0; j < y; j++)
        {
            array3D[i][j] = allElements + (i * y * z) + (j * z);
        }
    }

    //  Access array elements
    for(i = 0; i < x; i++)
    {
        printf("%d\n", i);

        for(j = 0; j < y; j++)
        {
            printf("\n");

            for(k = 0; k < z; k++)
            {
                array3D[i][j][k] = (i * y * z) + (j * z) + k;
                printf("\t%d", array3D[i][j][k]);
            }
        }

        printf("\n\n");
    }

    //  Deallocate 3D array
    free(allElements);
    for(i = 0; i < x; i++)
    {
        free(array3D[i]);
    }
    free (array3D);
}

For more details see this link 3d array

Ashwani K
  • 7,880
  • 19
  • 63
  • 102
0

This is a version of the idea in the question but using only one malloc, inspired by the other answers. It allows intuitive use of the square brackets and easy cleaning. I hope it does not make any compiler implementation specific assumption.

int main(int argc, char *argv[])
{
  int **array, i, j;
  array = allocate2d(3, 4);
  for (i = 0; i < 3; i++)
  {
    for (j = 0; j < 4; j++)
    {
      array[i][j] = j + i + 1;
    }
  }
  for (i = 0; i < 3; i++)
  {
    for (j = 0; j < 4; j++)
    {
      printf("array[%d][%d] = %d\n", i, j, array[i][j]);
    }
  }
  free(array);
  return EXIT_SUCCESS;
}

int **allocate2d(int x, int y)
{
  int i;
  int **array = malloc(sizeof(int *) * x + sizeof(int) * x * y);
  for (i = 0; i < x; i++)
  {
    array[i] = ((int *)(array + x)) + y * i;
  }
  return array;
}
Palas
  • 13
  • 3