Multi-dimensional arrays in C

This page tries to explain a common misconseption in using multi-dimensional arrays in C.

What you CAN do with C

C allows you to create a 2D array simply by using the square bracket nomenclature. For example:

   int array[MAXI][MAXJ];

It then allows you to use a 'slice' if that array as if it were a 1D array. For example:

   for(i=0; i<MAXI; i++)
   {
      printline(array[i], MAXJ);
   }

where the function printline() takes a pointer:

   void printline(int *line, int len)

Here is a complete sample program which demonstrates this.

What you CAN'T do with C

So, a logical extension of this is that if you wanted to provide a routine which printed the whole array rather than a line at a time, you should be able to do:

   int array[MAXI][MAXJ];
   ...
   printarray(array, MAXI, MAXJ);

with the printarray() function taking a pointer-to-pointer:

   void printarray(int **array, int maxx, int maxy)

However, this does not work. The compiler should complain that array is of the wrong type. You could get around this by casting it to int **, but if you do that the program core dumps. Here's the complete source so you can try it for yourself. (Define CAST if you want to try it with the cast.)

Fix the array sizes

The reason this is the case is that, despite what you might think (and what many C books imply), the first dimension of the 2D array is not an array of pointers. Instead the whole 2D array exists as a single block of data and the compiler simply calculates offsets to access a particular cell. When you pass the 2D array like this, the function you call doesn't know what the offsets are.

The answer, therefore, is that instead of passing the 2D array with a pointer-to-pointer, you have to pass it as an array with dimensions specified in the function:

   printarray(array, MAXI, MAXJ);

with the function defined as:

   void printarray(int array[MAXI][MAXJ], int maxx, int maxy)

Here is some sample code that works properly.

Use malloc

This is all very well and is absolutely fine if you are doing this in a standalone piece of C source code. But what happens if you wish to place the printarray() function in a library so it can be used by many programs which need to pass it arrays of different sizes? It won't work!

The answer is to use malloc() to allocate your 2D arrays so that the first dimension genuinely is an array of pointers:

   if((array = (int **)malloc(MAXI * sizeof(int *)))==NULL)
   {
      return(1);
   }
   
   for(i=0; i<MAXI; i++)
   {
      if((array[i] = (int *)malloc(MAXJ * sizeof(int)))==NULL)
      {
         return(1);
      }
   }

then you can call the routine with:

   printarray(array, MAXI, MAXJ);

and define it as:

   void printarray(int **array, int maxx, int maxy)

Here is a sample program which demonstrates this.

My bioplib libraries provide routines Array2D() and Array3D() which do all the allocation for you.

Another example

Further proof of the way that C 'tricks' you into thinking that arrays without the square brackets are the same as pointers comes from another example where one uses a 1-D array.

One often reads that if you have an array which has been defined as:

   int array1D[MAXJ];

then, if you use just array1D, you have a pointer to the array. This is not strictly the case!

Suppose we have the printarray() routine used above which prints out the contents of a 2D array, but we want to use it to print a 1D array. If the name of the 1D array is indeed a pointer, then all we should need to do is get a reference to this pointer (i.e. a pointer-to-pointer) using & and pass that. i.e.:

   int array1d[MAXJ];
   ...
   printarray(&array1d, 1, MAXJ);

Once again, however, this doesn't work - the compiler will complain about incompatible pointer types, if you cast &array1d to int ** then run the program it core dumps. You can try the code.

As before, the answer is to create the 1D array with malloc():

   int *array1d;
   if((array1d = (int *)malloc(MAXJ * sizeof(int)))==NULL)
   {
      return(1);
   }
   ...
   printarray(&array1d, 1, MAXJ);

You can download the complete example code and try it yourself.