23

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?

1

7 Answers 7

25

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);
}
Sign up to request clarification or add additional context in comments.

2 Comments

but how do you cast the result of malloc(sizeof *arr * rows) to int (*arr)[cols] ?
@SergeiK: just saw your comment -- no cast is necessary. arr is just a pointer like any other.
9

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;

Comments

7

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.

2 Comments

Code that uses this still has to go through the extra indirection of the array of row-pointers. I wouldn't recommend this unless you want to be able to swap rows by rearranging pointer elements.
And BTW, compilers will normally do the "avoiding multiplication" optimization for you, doing tmp += COLS instead of i*COLS. en.wikipedia.org/wiki/Strength_reduction. Your way of writing it as A[i-1]+COLS should also optimize ok, but I wouldn't bother.
3

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]"

Comments

2

The best way is to allocate a pointer to an array,

int (*a)[cols] = malloc(rows * sizeof *a);
if (a == NULL) {
    // alloc failure, handle or exit
}

for(int i = 0; i < rows; ++i) {
    for(int j = 0; j < cols; ++j) {
        a[i][j] = i+j;
    }
}

If the compiler doesn't support variable length arrays, that only works if cols is a constant expression (but then you should upgrade your compiler anyway).

1 Comment

Phrasing: this is allocating space for a 2D array, or more simply "allocating a true 2D array". The pointer to it (a) is a local variable in automatic storage, so doesn't have to be manually allocated.
2

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
.
.
.

Comments

1

You can typedef your array (for less headake) and then do something like that:

#include <stdlib.h>
#define N 10
typedef int A[N][N];
int main () {
  A a; // on the stack
  a[0][0]=1;
  A *b=(A*)malloc (sizeof(A)); // on the heap
  (*b)[0][0]=1;
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.