dynamic allocation/deallocation of 2D & 3D arrays
-
22-07-2019 - |
Question
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);
}
Solution
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.)
OTHER TIPS
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.
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
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;
}