0

Creating two dimensional arrays in C is easy:

char (*arr)[50] = malloc(sizeof(arr) * 10 * 50); // 10x50 matrix

How do you do three dimensional arrays in C? It doesn't look like I can do something like:

char (**arr)[50] = malloc(sizeof(arr) * 10 * 20 * 50); // 10x20x50 matrix?
bodacydo
  • 75,521
  • 93
  • 229
  • 319

4 Answers4

3

Three dimensional array requires 2 dimensions to be known

char (*arr)[20][50] = malloc(sizeof(char) * 10 * 20 * 50)

Note: I have corrected sizeof(arr) to sizeof(char), because sizeof(arr) will return the size of a pointer.

user1969104
  • 2,340
  • 14
  • 15
  • 2
    `sizeof(char)` is always superfluous... – Deduplicator Jul 21 '14 at 21:12
  • Yes, I agree. I tend to do this for readability to identify the type allocated. Or more better would be to say malloc(sizeof(char[10][20][30])). – user1969104 Jul 21 '14 at 21:13
  • 1
    In that case, you are doing it wrong. `sizeof ***arr` makes some sense, but that *documentation* in the best case becomes stale and *not* wrong. – Deduplicator Jul 21 '14 at 21:14
  • Thanks for the excellent response! Now I've another question, what if `20` and `30` are not constants but values passed to a function? – bodacydo Jul 21 '14 at 21:16
  • 1
    @bodacydo: you should really just follow this recipe here and it will do everything you want - http://stackoverflow.com/a/2306802/2591612 – Engineer2021 Jul 21 '14 at 21:22
  • 1
    @bodacydo: If you think it is an excellent response, then do the author of the response a favour and upvote it. – Rudy Velthuis Jul 21 '14 at 21:24
  • @bodacydo, If the array subscripts are variables, you could do similar syntax in C99, which supports variable sized array. Otherwise you need to manage the 3-dimensions yourselves by allocating a single dimensional array. – user1969104 Jul 21 '14 at 21:29
  • 1
    `malloc( sizeof *arr * 10)` would be better. – John Bode Jul 21 '14 at 22:04
1

A possible way could be to allocate a mono-dimensional array, e.g.

 int width=10; length=20; height=50;
 char* arr = malloc(width*length*height);
 if (!arr) { perror("malloc"); exit(EXIT_FAILURE); };

then have some way to access it, for instance a macro

 #define Element(I,J,K) arr[width*length*(I)+length*(J)+(K)]

and use Element(i,j,k)

You could pack all this using a flexible array member like

 struct my3dstring_st {
   int width;
   int length;
   int height;
   char arr[];
 };

then have a.g. a making function

  struct my3dstring_st *
  make_my3dstring (int width, int length, int height)
  { 
    if (width<=0 || length<=0 || height<=0) return NULL;
    struct my3dstring_st* s = 
       malloc(sizeof(struct my3dstring_st) 
              + width * length * height);
    if (!s) {perror("malloc"); exit(EXIT_FAILURE); };
    s->width = width;
    s->length = length;
    s->height = height;
    memset (s->arr, 0, width * length * height);
    return s;
  }

and an inline accessing function (in a header file):

  static inline int 
  access_m3dstring(struct my3dstring_st*s, int i, int j, int k) {
     if (!s || i<0 || j<0 || k<0 
         || i>=s->width || j>=s->height || k>=s->length) return EOF;
     return s->arr[i*->width*s->height + j*s->height + k];
   }

I leave as an exercise to write the modification function modify_m3dstring, and you could have unsafe but faster variants which don't do any checks...

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • Thanks this is very cool! Modification function can be as simple as `Element[i,j,k] = x;` :) – bodacydo Jul 21 '14 at 21:37
  • Not if you want to modify some `struct my3dstring_st*` pointer passed as argument. My macro `Element` only works if `arr` is a *global* variable (or a formal parameter but that is ugly). – Basile Starynkevitch Jul 21 '14 at 21:38
  • `static inline int access_m3dstring(struct my3dstring_st*s, int i, int j, int k, char val) { if (!s || i<0 || j<0 || k<0 || i>=s->width || j>=s->height || k>=s->length) return EOF; s->arr[i*->width*s->height + j*s->height + k] = val; }` – bodacydo Jul 21 '14 at 21:52
  • 1
    I mean edit_m3dstring! `s->arr[i*->width*s->height + j*s->height + k] = val`. Is the relevant part. And also `char val` in function specification. – bodacydo Jul 21 '14 at 22:08
  • 1
    `calloc` is probably better than `malloc` + `memset`. Some operating systems have a more optimal way of providing zero-initialized blocks. – M.M Jul 21 '14 at 22:43
1

General rules:

T *arr         = malloc( sizeof *arr * n ); // for an N-element array
T (*arr)[N]    = malloc( sizeof *arr * m ); // for an NxM-element array
T (*arr)[N][M] = malloc( sizeof *arr * k ); // for an NxMxK-element array

where uppercase letters indicate values that are known at compile time and lowercase letters indicate values that are known at run time. The pattern for higher-dimensional arrays should be obvious.

If you are using C99 compiler or a C2011 compiler that supports variable-length arrays, you can use runtime variables for all your dimensions:

size_t n = some_value();
size_t m = some_other_value();
size_t k = yet_another_value();

T (*arr)[n][m] = malloc( sizeof *arr * k );

The type of the expression *arr is T [n][m], so sizeof *arr gives the same result as sizeof (T) * n * m; the result is easier to read and less prone to errors.

If your compiler doesn't support VLAs and you don't know your dimensions at compile time, you'll either have to allocate as a 1-d array and compute offsets manually:

T *arr = malloc( sizeof *arr * n * m * k );
...
arr[ 3*n*m + 2*m + 1] = x; // equivalient to arr[3][2][1] = x

Or, if you can live with your rows not being adjacent in memory, you could allocate the array piecemeal:

T ***arr = malloc (sizeof *arr * n );
for (size_t i = 0; i < n; i++ )
{
  arr[i] = malloc( sizeof *arr[i] * m );
  for (size_t j = 0; j < m; j++ )
  {
    arr[i][j] = malloc( sizeof *arr[i][j] * k )
  }
}

Ideally, you should check the result of each malloc to make sure it succeeded. And you'll have to free the array in the reverse order that you allocated it:

John Bode
  • 119,563
  • 19
  • 122
  • 198
0
char (*arr)[20][50] = malloc(sizeof(char) * 10 * 20 * 50);

sizeof(char) is guaranteed to be 1. So, it can be omitted.

char (*arr)[20][50] = malloc(10 * 20 * 50);
R Sahu
  • 204,454
  • 14
  • 159
  • 270