You can malloc each dimension separately as a single contiguous block (so a 4D array would be just 4 mallocs).

Example for 2D

Code:

#include <stdio.h>
#include <stdlib.h>
int **allocate2Darray ( int rows, int cols ) {
int **result;
int r, *rp; // initialise each row pointer
result = malloc( rows * sizeof *result );
result[0] = malloc( rows * cols * sizeof *result[0] );
for ( r = 0, rp = result[0] ; r < rows ; r++, rp += cols ) {
result[r] = rp;
}
return result;
}
void free2Darray ( int **array ) {
free( array[0] );
free( array );
}
#define MAX_R 5
#define MAX_C 16
int main(int argc, char **argv) {
int **a = allocate2Darray( MAX_R, MAX_C );
int r, c;
for ( r = 0 ; r < MAX_R ; r++ ) {
for ( c = 0 ; c < MAX_C ; c++ ) {
a[r][c] = 0;
}
}
printf( "The pitch between rows is %x bytes\n", MAX_C * sizeof(int) );
for ( r = 0 ; r < MAX_R ; r++ ) {
printf( "Row %d begins at %p\n", r, (void*)a[r] );
}
free2Darray( a );
return 0;
}

3D would have a malloc for result[0][0] and so on.

It's quite nice in that it doesn't involve any pointer casting and do-it-yourself pointer arithmetic to make it work