How to dynamically allocate a contiguous block of memory for a 2D array
If I allocate a 2D array like this int a[N][N]; it will allocate a contiguous block of memory.
But if I try to do it dynamically like this :
int **a = malloc(rows * sizeof(int*));
for(int i = 0; i < rows; i++)
a[i] = malloc(cols * sizeof(int));
This maintains a unit stride between the elements in the rows, but this may not be the case between rows.
One solution is to convert from 2D to 1D, besides that, is there another way to do it?
If your array dimensions are known at compile time:
#define ROWS ...
#define COLS ...
int (*arr)[COLS] = malloc(sizeof *arr * ROWS);
if (arr)
{
// do stuff with arr[i][j]
free(arr);
}
If your array dimensions are not known at compile time, and you are using a C99 compiler or a C2011 compiler that supports variable length arrays:
size_t rows, cols;
// assign rows and cols
int (*arr)[cols] = malloc(sizeof *arr * rows);
if (arr)
{
// do stuff with arr[i][j]
free(arr);
}
If your array dimensions are not known at compile time, and you are not using a C99 compiler or a C2011 compiler that supports variable-length arrays:
size_t rows, cols;
// assign rows and cols
int *arr = malloc(sizeof *arr * rows * cols);
{
// do stuff with arr[i * rows + j]
free(arr);
}
In fact, n-dimensional arrays (allocated on the stack) are really just 1-dimension vectors. The multiple indexing is just syntactic sugar. But you can write an accessor function to emulate something like what you want:
int index_array(int *arr, size_t width, int x, int y)
{
return arr[x * width + y];
}
const size_t width = 3;
const size_t height = 2;
int *arr = malloc(width * height * sizeof(*arr));
// ... fill it with values, then access it:
int arr_1_1 = index_array(arr, width, 1, 1);
However, if you have C99 support, then declaring a pointer to an array is possible, and you can even use the syntactic sugar:
int (*arr)[width] = malloc(sizeof((*arr) * height);
arr[x][y] = 42;
Say you want to dynamically allocate a 2-dimensional integer array of ROWS rows and COLS columns. Then you can first allocate a continuous chunk of ROWS * COLS integers and then manually split it into ROWS rows. Without syntactic sugar, this reads
int *mem = malloc(ROWS * COLS * sizeof(int));
int **A = malloc(ROWS * sizeof(int*));
for(int i = 0; i < ROWS; i++)
A[i] = mem + COLS*i;
// use A[i][j]
and can be done more efficiently by avoiding the multiplication,
int *mem = malloc(ROWS * COLS * sizeof(int));
int **A = malloc(ROWS * sizeof(int*));
A[0] = mem;
for(int i = 1; i < ROWS; i++)
A[i] = A[i-1] + COLS;
// use A[i][j]
Finally, one could give up the extra pointer altogether,
int **A = malloc(ROWS * sizeof(int*));
A[0] = malloc(ROWS * COLS * sizeof(int));
for(int i = 1; i < ROWS; i++)
A[i] = A[i-1] + COLS;
// use A[i][j]
but there's an important GOTCHA! You would have to be careful to first deallocate A[0] and then A,
free(A[0]);
free(A); // if this were done first, then A[0] would be invalidated
The same idea can be extended to 3- or higher-dimensional arrays, although the code will get messy.
You can treat dynamically allocated memory as an array of a any dimension by accessing it in strides:
int * a = malloc(sizeof(int) * N1 * N2 * N3); // think "int[N1][N2][N3]"
a[i * N2 * N3 + j * N3 + k] = 10; // like "a[i, j, k]"
Excuse my lack of formatting or any mistakes, but this is from a cellphone.
I also encountered strides where I tried to use fwrite() to output using the int** variable as the src address.
One solution was to make use of two malloc() invocations:
#define HEIGHT 16
#define WIDTH 16
.
.
.
//allocate
int **data = malloc(HEIGHT * sizeof(int **));
int *realdata = malloc(HEIGHT * WIDTH * sizeof(int));
//manually index
for (int i = 0; i < HEIGHT; i++)
data[i] = &realdata[i * WIDTH];
//populate
int idx = 0;
for (int i = 0; i < HEIGHT; i++)
for (int j = 0; j < WIDTH; j++)
data[i][j] = idx++;
//select
int idx = 0;
for (int i = 0; i < HEIGHT; i++)
{
for (int j = 0; j < WIDTH; j++)
printf("%i, ", data[i][j]);
printf("/n");
}
//deallocate
.
.
.