98

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]);

0

4 Answers 4

161

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?.)

Sign up to request clarification or add additional context in comments.

7 Comments

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.
void func_vla(int array[rows][cols], int rows, int cols) should be void func_vla(int rows, int cols, int array[rows][cols])
@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.
malloc returns a void pointer, you sure you don't need to cast it into int* or int** as and when required? Solution 2
Casting is more of a C++ convention, got that! Thanks @BartvanIngenSchenau
|
25

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.

10 Comments

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)?
@KartikChhajed Your code requires implicit conversion from int*[3] to int**, which might not be allowed for most C/C++ compilers.
@jwpol unfortunately it is not a good idea: stackoverflow.com/q/25303647/1606345
@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 exampleint 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.
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
|
17

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);
  ...
}

5 Comments

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.
data dont get lost means without using malloc. Thanks for the help.
@Christian Mann: Thats a good factoid. I happened to write an elaborate explanation on it today :-) stackoverflow.com/questions/3906777/…
It seems everyone is having problems with multi-dimensional arrays today. :) I wrote a similar explanation in another question too: stackoverflow.com/questions/3911244/…
@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.
5

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)
{

}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.