Manipulate multidimensional array in a function

21,951

Solution 1

Not exactly sure, what the problem is but this works (and prints the value "9"):

#include <stdio.h>

#define ROWS 10
#define COLUMNS 10

void func2(int multarray[][COLUMNS]){
        multarray[1][4]=10;
}

void func1(int multarray[][COLUMNS]){
        multarray[0][3]=9;
        func2(multarray);
}

int main(){

        int multarray[ROWS][COLUMNS];
        func1(multarray);
        printf("%d\n", multarray[0][3]);
        printf("%d\n", multarray[1][4]);
        return 0;
}

Notice, that the array decays to a pointer when passed to a function.

Solution 2

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.

Solution 3

For your 2 dimensional arrays I would define a type for it.

typedef int my2DArray_t[ROWS][COLUMNS];

You can then declare variables of this type and pointers to them. This makes it easier to pass things around.

void someFuncOther (my2DArray_t *someArray)
    {

    /* Set some values in array */
    (*someArray)[1][1] = 4;

    }

void someFunc (my2DArray_t *someArray)
    {

    /* Set some values in array */
    (*someArray)[1][0] = 7;

    /* Have someFuncOther do some more work */
    someFuncOther (someArray);

    }


int main (void)
    {
    /* This is the actual array */
    my2DArray_t myArray;

    /* Set some values in array */
    myArray[0][2] = 6;

    /* Have someFunc do some work on myArray by passing a pointer to it */
    someFunc (&myArray);

    }
Share:
21,951
Dchris
Author by

Dchris

Related to computer science.

Updated on July 20, 2022

Comments

  • Dchris
    Dchris almost 2 years

    I read a lot of stuff in here and tried many but i couldn't find a way to pass a multidimensional array to a function in C, change some of the values and somehow return the new array. It's important to find a way to pass that array further to another function and do the same thing.

    I would like to find a way to pass the array to a function.Then pass it from the first function to a second one,do something there(maybe print,maybe change values),then use it again to the first function and finally use that array in main.

    My last try is:

    void func(int multarray[][columns]){
        multarray[0][0]=9;
    }
    
    int main(){
        int rows;
        int columns;
        int multarray[rows][columns];
        func(multarray);
        return 0;
    }
    

    I also tried this:

    void func(int multarray[rows][columns]){
        multarray[0][0]=9;
    }
    
    int main(){
        int rows;
        int columns;
        int multarray[rows][columns];
        func(multarray);
        return 0;
    }
    

    I also tried this:

    int
    getid(int row, int x, int y) {
              return (row*x+y);
    }
    
    void
    printMatrix(int*arr, int row, int col) {
         for(int x = 0; x < row ; x++) {
                 printf("\n");
                 for (int y = 0; y <col ; y++) {
                     printf("%d  ",arr[getid(row, x,y)]);
                 }
         }
    }
    
    main()
    {
    
        int arr[2][2] = {11,12,21,22};
        int row = 2, col = 2;
    
        printMatrix((int*)arr, row, col);
    
    }
    

    from here

    I also tried double pointers.I also read that there is a different approach if the compiler does not support VLAs. I am using gnu.