90

I need to do this to persist operations on the matrix as well. Does that mean that it needs to be passed by reference?

Will this suffice?

void operate_on_matrix(char matrix[][20]);

Piper
  • 1,266
  • 3
  • 15
  • 26
Shweta
  • 5,198
  • 11
  • 44
  • 58

4 Answers4

151

C does not really have multi-dimensional arrays, but there are several ways to simulate them. The way to pass such arrays to a function depends on the way used to simulate the multiple dimensions:

1) Use an array of arrays. This can only be used if your array bounds are fully determined at compile time, or if your compiler supports VLA's:

#define ROWS 4
#define COLS 5

void func(int array[ROWS][COLS])
{
  int i, j;

  for (i=0; i<ROWS; i++)
  {
    for (j=0; j<COLS; j++)
    {
      array[i][j] = i*j;
    }
  }
}

void func_vla(int rows, int cols, int array[rows][cols])
{
  int i, j;

  for (i=0; i<rows; i++)
  {
    for (j=0; j<cols; j++)
    {
      array[i][j] = i*j;
    }
  }
}

int main()
{
  int x[ROWS][COLS];

  func(x);
  func_vla(ROWS, COLS, x);
}

2) Use a (dynamically allocated) array of pointers to (dynamically allocated) arrays. This is used mostly when the array bounds are not known until runtime.

void func(int** array, int rows, int cols)
{
  int i, j;

  for (i=0; i<rows; i++)
  {
    for (j=0; j<cols; j++)
    {
      array[i][j] = i*j;
    }
  }
}

int main()
{
  int rows, cols, i;
  int **x;

  /* obtain values for rows & cols */

  /* allocate the array */
  x = malloc(rows * sizeof *x);
  for (i=0; i<rows; i++)
  {
    x[i] = malloc(cols * sizeof *x[i]);
  }

  /* use the array */
  func(x, rows, cols);

  /* deallocate the array */
  for (i=0; i<rows; i++)
  {
    free(x[i]);
  }
  free(x);
}

3) Use a 1-dimensional array and fixup the indices. This can be used with both statically allocated (fixed-size) and dynamically allocated arrays:

void func(int* array, int rows, int cols)
{
  int i, j;

  for (i=0; i<rows; i++)
  {
    for (j=0; j<cols; j++)
    {
      array[i*cols+j]=i*j;
    }
  }
}

int main()
{
  int rows, cols;
  int *x;

  /* obtain values for rows & cols */

  /* allocate the array */
  x = malloc(rows * cols * sizeof *x);

  /* use the array */
  func(x, rows, cols);

  /* deallocate the array */
  free(x);
}

4) Use a dynamically allocated VLA. One advantage of this over option 2 is that there is a single memory allocation; another is that less memory is needed because the array of pointers is not required.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

extern void func_vla(int rows, int cols, int array[rows][cols]);
extern void get_rows_cols(int *rows, int *cols);
extern void dump_array(const char *tag, int rows, int cols, int array[rows][cols]);

void func_vla(int rows, int cols, int array[rows][cols])
{
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            array[i][j] = (i + 1) * (j + 1);
        }
    }
}

int main(void)
{
    int rows, cols;

    get_rows_cols(&rows, &cols);

    int (*array)[cols] = malloc(rows * cols * sizeof(array[0][0]));
    /* error check omitted */

    func_vla(rows, cols, array);
    dump_array("After initialization", rows, cols, array);

    free(array);
    return 0;
}

void dump_array(const char *tag, int rows, int cols, int array[rows][cols])
{
    printf("%s (%dx%d):\n", tag, rows, cols);
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
            printf("%4d", array[i][j]);
        putchar('\n');
    }
}

void get_rows_cols(int *rows, int *cols)
{
    srand(time(0));           // Only acceptable because it is called once
    *rows = 5 + rand() % 10;
    *cols = 3 + rand() % 12;
}

(See srand() — why call it only once?.)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Bart van Ingen Schenau
  • 15,488
  • 4
  • 32
  • 41
  • In the first way mentioned above, the code will not compile. "rows" and "cols" in lines 17 and 35 must change to "ROWS" and "COLS", respectively. – KZcoding May 27 '15 at 15:35
  • 7
    `void func_vla(int array[rows][cols], int rows, int cols)` should be `void func_vla(int rows, int cols, int array[rows][cols])` – David Ranieri Oct 12 '15 at 16:55
  • @KZcoding: The VLA notation used in lines 17 and 35 is correct if the compiler supports C99, or if it supports C11 and doesn't define `__STDC_NO_VLA__`. If the compiler does not support VLAs, then of course it does not compile. – Jonathan Leffler Dec 26 '18 at 16:45
  • malloc returns a void pointer, you sure you don't need to cast it into int* or int** as and when required? Solution 2 – CocoCrisp Mar 05 '19 at 19:22
  • Casting is more of a C++ convention, got that! Thanks @BartvanIngenSchenau – CocoCrisp Mar 05 '19 at 20:16
  • What reference are you using? Where is this information documented? – young_souvlaki Jun 22 '20 at 18:56
  • Also, mentioning that both `func` and `func_vla` will change the array because the reference is passed. – Matin Zivdar Aug 21 '22 at 18:10
21

Easiest Way in Passing A Variable-Length 2D Array

Most clean technique for both C & C++ is: pass 2D array like a 1D array, then use as 2D inside the function.

#include <stdio.h>

void func(int row, int col, int* matrix){
    int i, j;
    for(i=0; i<row; i++){
        for(j=0; j<col; j++){
            printf("%d ", *(matrix + i*col + j)); // or better: printf("%d ", *matrix++);
        }
        printf("\n");
    }
}

int main(){
    int matrix[2][3] = { {0, 1, 2}, {3, 4, 5} };
    func(2, 3, matrix[0]);

    return 0;
}

Internally, no matter how many dimensions an array has, C/C++ always maintains a 1D array. And so, we can pass any multi-dimensional array like this.

Minhas Kamal
  • 20,752
  • 7
  • 62
  • 64
  • Just little a query: If we call the function like `func(2, 3, matrix)`, then we should have `void func(int row, int col, int** matrix)`? – Kartik Chhajed Sep 07 '20 at 13:36
  • @KartikChhajed Your code requires implicit conversion from `int*[3]` to `int**`, which might not be allowed for most C/C++ compilers. – Minhas Kamal Oct 24 '20 at 05:55
  • @jwpol unfortunately it is not a good idea: https://stackoverflow.com/q/25303647/1606345 – David Ranieri Oct 24 '20 at 07:55
  • 1
    @David Ranieri If you know the dimension of the array, I can't see any reason why you shouldn't dereference a particular element. For example```int matrix[2][2]```, occupies 4 "cells" of memory, so from ```*(p)``` to ```*(p+3)``` (where p is an address of the first element) are within the dimension of allocated memory. Same in this answer. The thread which you posted is about dereferencing one element after the last one in the array, which is not the case here. The answer provided by @Minhas Kamal is absolutely safe providing that you passes legitimate boundaries in ```raw``` and ```col```. – jwpol Oct 24 '20 at 15:08
  • evaluation of `*(matrix + i*col + j)` for `i`/`j` equal to 0/1 invokes UB. The type of object pointer by `matrix` is `int[2]`. Thus `*(matrix + 2)` is incorrect – tstanisl Dec 10 '21 at 11:51
  • @tstanisl Sorry, I could not understand your example. Would you please explain a little? – Minhas Kamal Dec 10 '21 at 14:17
  • I mean that matrix[0] is transformed to the pointer of the first element of the first row. The row of **three**-element-long matrix. Therefore it cannot be accessed at index larger than 2 without invoking UB – tstanisl Dec 10 '21 at 14:31
  • I now understand what you mean. In theory, you are right :) The point of this solution is- In C/C++, support for multi-dimensional arrays is like a syntactic sugar. At memory level, it is always a 1D array. So practically, for all standard C/C++ compilers, this code should work. Please do share if you can think of a practical situation where this code will fail. – Minhas Kamal Dec 10 '21 at 17:41
  • I generally agree with you. Drop a note to the answer about potential UB – tstanisl Dec 13 '21 at 15:52
  • To my knowledge, I don't know of any case that could cause UB. And also, I don't want to be too general (and vague) and just say "potential UB", as people will only get confused. If I add the risk, I would want to be specific and add the exact scenario as well. – Minhas Kamal Dec 14 '21 at 05:05
14

I don't know what you mean by "data dont get lost". Here's how you pass a normal 2D array to a function:

void myfunc(int arr[M][N]) { // M is optional, but N is required
  ..
}

int main() {
  int somearr[M][N];
  ...
  myfunc(somearr);
  ...
}
casablanca
  • 69,683
  • 7
  • 133
  • 150
  • 28
    Random factoid: The reason N is required is because the computer needs to know how far along to increment the pointer for each "row". Really, all dimensions except the first one are necessary. C stores arrays as chunks of memory, with no delimiters. – Christian Mann Oct 12 '10 at 03:12
  • data dont get lost means without using malloc. Thanks for the help. – Shweta Oct 12 '10 at 03:19
  • 1
    @Christian Mann: Thats a good factoid. I happened to write an elaborate explanation on it today :-) http://stackoverflow.com/questions/3906777/error-defining-and-initializing-multidimensional-array/3910533#3910533 – Arun Oct 12 '10 at 05:30
  • It seems everyone is having problems with multi-dimensional arrays today. :) I wrote a similar explanation in another question too: http://stackoverflow.com/questions/3911244/confusion-about-pointers-and-multidimensional-arrays – casablanca Oct 12 '10 at 05:34
  • 2
    @ChristianMann Rather, the reason the array syntax at all works, is because the compiler adjusts the array declaration in the parameter to a pointer to the first element, in this case `int (*)[N]`. And that is also why all dimensions but the outer-most one have to be provided - the array only decays once. This has absolutely nothing to do with what "the computer needs to know" - nonsense. Compare it with the 1D case: `void func (int [n])`, which gets adjusted to `void func (int*)` and all size information is lost - "the computer" doesn't get to know a thing and the compiler couldn't care less. – Lundin Apr 10 '17 at 14:46
3

2D array:

int sum(int array[][COLS], int rows)
{

}

3D array:

int sum(int array[][B][C], int A)
{

}

4D array:

int sum(int array[][B][C][D], int A)
{

}

and nD array:

int sum(int ar[][B][C][D][E][F].....[N], int A)
{

}
shinxg
  • 490
  • 4
  • 9