Manipulate multidimensional array in a function

Several things to remember:

  1. When you pass an array expression as an argument to a function, it will be converted from an expression of type “N-element array of T” to “pointer to T“, and the value of the expression will be the address of the first element of the array. The called function receives a pointer value.

  2. The [] operator can be used with expressions of array or pointer type; IOW, given the declarations int a[10]; int *p = a;, then p[i] and a[i] refer to the same element.

  3. When declaring a function that accepts a VLA as a parameter, you must declare the parameters that specify dimension before you declare the array.

So, for a function that manipulates a 2D VLA, you’d write something like

void foo( size_t rows, size_t cols, int (*multiarray)[cols] ) // or multiarray[][cols]
{
   size_t i, j;

   for ( i = 0; i < rows; i++ )
     for ( j = 0; j < cols; j++ )
       multiarray[i][j] = some_value();
}

What’s up with int (*multiarray)[cols]? Remember that in passing the array expression as an argument, the type of the array expression is converted from “N-element array of T” to “pointer to T“. In this case, T is “cols-element array of int“, so we’re going from “rows-element array of cols-element aray of int” to “pointer to cols-element array of int“. In the context of a function parameter declaration, T a[N], T a[], and T *a are all identical; in all three cases, a is declared as a pointer to T. So int (*multiarray)[cols] is equivalent to int multiarray[][cols], which is equivalent to int multiarray[rows][cols]. I prefer using the first form because it most accurately represents the situation.

If you want to pass this array as an argument to another function, you’d use the same type:

void bar( size_t rows, size_t cols, int (*multiarray)[cols] )
{
   foo( rows, cols, multiarray );
}

int main( void )
{
  size_t rows = 0;
  size_t cols = 0;

  // you must assign values to rows and cols before declaring a VLA with them
  rows = ...;
  cols = ...;

  int arr[rows][cols];

  bar( rows, cols, arr );
  ...
}

Any changes to the array contents made in foo will be reflected in bar and main.

VLAs can be useful, but they have their limitations. They can’t be declared static, nor can they be defined outside of a function. They cannot use {}-style initialization syntax. Also, VLA support is now optional as of the 2011 standard, so you can’t rely on them being supported everywhere.

In the event you don’t have VLAs available and your array size isn’t known until runtime, you’ll have to use dynamic memory allocation (malloc or calloc), and the types you pass to your functions will be different:

void foo( size_t rows, size_t cols, int **multiarray )
{
  size_t i, j;

  for ( i = 0; i < rows; i++ )
    for ( j = 0; j < cols; j++ )
      multiarray[i][j] = some_value();

}

void bar( size_t rows, size_t cols, int **multiarray )
{
  foo( rows, cols, multiarray );
}

int main( void )
{
  size_t rows; 
  size_t cols;
  int **multiarray = NULL;

  ... // get rows and cols

  // allocate memory for pointers to each row
  multiarray = malloc( sizeof *multiarray * rows );
  if ( multiarray )
  {
    size_t i;
    // allocate each row
    for ( i = 0; i < rows; i++ )
    {
      multiarray[i] = malloc( sizeof *multiarray[i] * cols );
      if ( !multiarray[i] )
        break;
    }

    if ( i < rows )
    {
      // malloc failed for one of the multiarray rows; we need to 
      // free whatever memory has already been allocated and exit
      while ( i-- )
        free( multiarray[i] );
      free( multiarray );
      exit(0);
    }
  }

  bar ( rows, cols, multiarray );
  ...

  if ( multiarray )
  {
    size_t i;

    for ( i = 0; i < rows; i++ )
      free( multiarray[i] );
    free( multiarray );
  }
}

One drawback with this approach is that the allocated memory isn’t guaranteed to be contiguous (i.e., rows won’t be adjacent in memory). If that matters, you’ll have to go with yet another approach. Instead of allocating rows and columns separately, you allocate everything in one single block, and manually map array indices:

void foo( size_t rows, size_t cols, int *fakemultiarray )
{
  size_t i, j;

  for ( i = 0; i < rows; i++ )
    for ( j = 0; j < rows; j++ )
       fakemultiarray[ i * rows + j ] = some_value();
}

void bar( size_t rows, size_t cols, int *fakemultiarray )
{
  foo( rows, cols, fakemultiarray );
}

int main( void )
{
  size_t rows;
  size_t cols;
  int *fakemultiarray = NULL;

  ... // get rows and cols

  fakemultiarray = malloc( sizeof *fakemultiarray * rows * cols );
  if ( fakemultiarray )
    bar( rows, cols, fakemultiarray );

  ...
  free( fakemultiarray );
}

In this case, we’ve allocated a single buffer large enough for all elements, but we have to index it as a 1D array, computing the index as i * rows + j.

Leave a Comment