3

How to pass by reference multidimensional array with unknown size in C or C++?

EDIT:

For example, in main function I have:

int main(){
    int x, y;
    int arr[x][y];
    // pass_by_ref(/* passing just arr[][] by reference */);
}  

and the function:

void pass_by_ref(/* proper parameter for arr[][] */){
    // int size_x_Arr = ???
    // int size_y_arr = ???
}

How to implement the commented line?

Ifan Iqbal
  • 3,053
  • 5
  • 28
  • 31
  • Make your choice - in C++ you can use `std::vector&` or other similar types, in C you need to specify the size as additional parameter or as first element (if you're using a integral type). – Zeta Mar 06 '13 at 10:53
  • Actually, you can *only* pass an array by reference with unknown size! (OK, there's a trick with a struct, but let's not go there). What have you tried? – cdarke Mar 06 '13 at 10:53
  • In C it's impossible because C has no references. In C++, it's impossible because it doesn't support variable length arrays. –  Mar 06 '13 at 10:54
  • possible duplicate of [How can I pass a multidimensional array to a function?](http://stackoverflow.com/questions/3155188/how-can-i-pass-a-multidimensional-array-to-a-function) – Bo Persson Mar 06 '13 at 10:55
  • In C, in short, you don't. You must have a construct of *some* kind that dictates sizing at usage-time. How you gleen that size info is up to you. – WhozCraig Mar 06 '13 at 10:59
  • How do you exactly define yours "multidimensional array with unknown size" ? – qPCR4vir Mar 06 '13 at 11:00

5 Answers5

4

Simply put, you can't. In C, you can't pass by reference, since C has no references. In C++, you can't pass arrays with unknown size, since C++ doesn't support variable-lenght arrays.

Alternative solutions: in C99, pass a pointer to the variable-length array; in C++, pass a reference to std::vector<std::vector<T>>.

Demonstration for C99:

#include <stdio.h>

void foo(int n, int k, int (*arr)[n][k])
{
    int i, j;
    for (i = 0; i < n; i++) {
        for (j = 0; j < k; j++) {
            printf("%3d ", (*arr)[i][j]);
        }
        printf("\n");
    }
}

int main(int argc, char *argv[])
{
    int a = strtol(argv[1], NULL, 10);
    int b = strtol(argv[2], NULL, 10);

    int arr[a][b];
    int i, j;
    for (i = 0; i < a; i++) {
        for (j = 0; j < b; j++) {
            arr[i][j] = i * j;
        }
    }

    foo(a, b, &arr);

    return 0;
}

Demonstration for C++03:

#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>

void foo(std::vector < std::vector < int > > &vec)
{
    for (std::vector < std::vector < int > >::iterator i = vec.begin(); i != vec.end(); i++) {
        for (std::vector<int>::iterator j = i->begin(); j != i->end(); j++) {
            std::cout << *j << " ";
        }
        std::cout << std::endl;
    }
}

int main(int argc, char *argv[])
{
    int i = strtol(argv[1], NULL, 10);
    int j = strtol(argv[2], NULL, 10);

    srand(time(NULL));

    std::vector < std::vector < int > > vec;
    vec.resize(i);
    for (std::vector < std::vector < int > >::iterator it = vec.begin(); it != vec.end(); it++) {
        it->resize(j);
        for (std::vector<int>::iterator jt = it->begin(); jt != it->end(); jt++) {
            *jt = random() % 10;
        }
    }

    foo(vec);

    return 0;
}
  • What is the meaning of this line: `strtol(argv[1], NULL, 10);`? – Ifan Iqbal Mar 06 '13 at 11:46
  • I would suggest to use `boost::array` in C++ – AxelOmega Mar 06 '13 at 12:06
  • I try to use this `void foo(int n, int k, int (*arr)[n][k])` and got `error: use of parameter 'n' outside function body` – Ifan Iqbal Mar 12 '13 at 23:51
  • @IfanIqbal I **compiled and run** this code. Are you compiling with `-std=c99`? (Also, spotting one error in the code should not make you unaccept my answer immediately.) –  Mar 12 '13 at 23:54
3

H2CO3's solution will work for C99 or a C2011 compiler that supports VLAs. For C89 or a C2011 compiler that doesn't support VLAs, or (God forbid) a K&R C compiler, you'd have to do something else.

Assuming you're passing a contiguously allocated array, you can pass a pointer to the first element (&a[0][0]) along with the dimension sizes, and then treat it as a 1-D array, mapping indices like so:

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

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

int main( void )
{
  int arr[10][20];

  foo( &arr[0][0], 10, 20 );
  ...
  return 0;
}

This will work for arrays allocated on the stack:

T a[M][N];

and for dynamically allocated arrays of the form:

T (*ap)[N] = malloc( M * sizeof *ap );

since both will have contiguously allocated rows. This will not work (or at least, not be guaranteed to work) for dynamically allocated arrays of the form:

T **ap = malloc( M * sizeof *ap );
if (ap)
{
  size_t i;
  for (i = 0; i < M; i++)
  {
    ap[i] = malloc( N * sizeof *ap[i] );
  }
}

since it's not guaranteed that all the rows will be allocated contiguously to each other.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

This is a sort of comment to the good answer of @John Bode

This will not work (or at least, not be guaranteed to work) for dynamically allocated arrays of the form:

But this variant will:

T **ap = malloc( M * sizeof *ap );
if (ap) return NULL;     ---> some error atention
if (ap)
{
  ap[0] = malloc( M * N * sizeof *ap[i] );
  if (ap[0]) { free(ap); return NULL;}     ---> some error atention
  size_t i;
  for (i = 1; i < M; i++)
  {
    ap[i] = ap[0] + i * N;
  }
}

After use :

free(ap[0]);
free(ap);

for T being int you call foo exactly als for the array int ap[M][N];

  foo( &ap[0][0], M, N);

since you guaranteed that all the rows are allocated contiguously to each other. This allocation is a litter more efficient.

qPCR4vir
  • 3,521
  • 1
  • 22
  • 32
0

John Bode's explanation is very good, but there is a little mistake: it should be

i * cols + j

instead of

i * rows + j
-1

If you really want references, then it's only in C++.

En example of a two-dimensional int array passed by reference

void function_taking_an_array(int**& multi_dim_array);

But the reference doesn't have any advantage, so simply use :

void function_taking_an_array(int** multi_dim_array);

I would advice you to use a container to hold your array.

Jaffa
  • 12,442
  • 4
  • 49
  • 101
  • Ehum, no. `int **` is a pointer to pointer to int, it's not a 2-dimensional array. –  Mar 06 '13 at 10:58
  • which could represent a bidimensional array. Using predefined array size or semaphor value (or specifying dimensions as additional parameters). The best example for that is the `main` function, with the `char** argv` function representing an array of char array. – Jaffa Mar 06 '13 at 10:59
  • It certainly can *emulate* them, if you use `malloc()` and pass in sizes, and whatever. But (double) pointers are still not (two-dimenisional) arrays. –  Mar 06 '13 at 11:01