442

I have a function which I want to take, as a parameter, a 2D array of variable size.

So far I have this:

void myFunction(double** myArray){
     myArray[x][y] = 5;
     etc...
}

And I have declared an array elsewhere in my code:

double anArray[10][10];

However, calling myFunction(anArray) gives me an error.

I do not want to copy the array when I pass it in. Any changes made in myFunction should alter the state of anArray. If I understand correctly, I only want to pass in as an argument a pointer to a 2D array. The function needs to accept arrays of different sizes also. So for example, [10][10] and [5][5]. How can I do this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
RogerDarwin
  • 4,423
  • 3
  • 14
  • 4
  • 2
    cannot convert parameter 3 from 'double [10][10]' to 'double **' – RogerDarwin Jan 07 '12 at 03:43
  • 4
    The [accepted answer](http://stackoverflow.com/a/8767247/183120) shows only 2 techniques [its _(2)_ and _(3)_ are the same] but there're [4 unique ways of passing a 2D array to a function](http://stackoverflow.com/a/17569578/183120). – legends2k Mar 24 '14 at 11:44
  • 1
    Strictly speaking, yes, they aren't 2D arrays, but this convention (albeit leading to UB) of having an array of pointers, each pointing to (a 1D) array, seems to be prevalent :( Having a flattened 1D array of m x n length, with helper functions/class to emulate a 2D array is perhaps better. – legends2k Jul 12 '15 at 08:40
  • 1
    **EASIEST**- `func(int* mat, int r, int c){ for(int i=0; i – Minhas Kamal Jan 09 '19 at 04:24
  • Future reference: In short you can't pass variable sized 2d arrays int arr[m][n] to functions in c/cpp easily. work around is pass &arr[0][0] into a function func(int *arr) then do arr[i*n+j] to access arr[i][j] within func. Or you can pass define int **arr using new/malloc in cpp/c. Then pass to func(int **arr) where you can use arr[i][j] – router Dec 15 '22 at 07:01
  • "_However, calling myFunction(anArray) gives me an error._" note that you're supposed to give the error message. – starball May 10 '23 at 01:14

19 Answers19

532

There are three ways to pass a 2D array to a function:

  1. The parameter is a 2D array

    int array[10][10];
    void passFunc(int a[][10])
    {
        // ...
    }
    passFunc(array);
    
  2. The parameter is an array containing pointers

    int *array[10];
    for(int i = 0; i < 10; i++)
        array[i] = new int[10];
    void passFunc(int *a[10]) //Array containing pointers
    {
        // ...
    }
    passFunc(array);
    
  3. The parameter is a pointer to a pointer

    int **array;
    array = new int *[10];
    for(int i = 0; i <10; i++)
        array[i] = new int[10];
    void passFunc(int **a)
    {
        // ...
    }
    passFunc(array);
    
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
shengy
  • 9,461
  • 4
  • 37
  • 61
  • Can I ask if in the third example, for the array (aka matrix) is also available the notation `array[i][j]`? – Overflowh May 26 '13 at 10:39
  • 5
    @Overflowh You can get the elements of `array` with `array[i][j]` :) – shengy May 27 '13 at 03:15
  • 21
    For the 1st case, the parameter can be declared as `int (*a)[10]`. – Zachary Jun 13 '13 at 14:38
  • 10
    For the 2nd case, the parameter can be declared as `int **`. – Zachary Jun 14 '13 at 03:14
  • 1
    @Zack: You're right, there're only really two cases; one is a pointer-to-pointer and other being a single pointer to an integer array of size n i.e. `int (*a) [10]`. – legends2k Jul 10 '13 at 10:44
  • If I declare the 2 D array with its size and make use of the 3) method of passing the array as a parameter then I get a error – devsaw Sep 07 '13 at 14:47
  • @DamnDev: For the third method you cannot declare a 2D array since it depends on allocating an array of pointers (holded by a pointer to pointer variable) and then allocating memory for all of them so that they can be used as a 2D array. However, if you're using C++, you can pass a 2D array as-is. See section 1 in [my answer](http://stackoverflow.com/a/17569578/183120) below. – legends2k Oct 07 '13 at 02:21
  • If we dynamically allocate matrix , and matrix is local to function main, can we still pass this matrix to some function f(datatype mat[][]) – aibotnet Feb 18 '15 at 18:04
  • Case 1: the parameter *isn't* a 2D array. – juanchopanza Mar 31 '15 at 05:54
  • Case 2: an array containing pointers isn't a 2D array. It is a 1D array. Of pointers. – juanchopanza Mar 31 '15 at 06:06
  • 3
    i would add a 4. using a `vector>` – 463035818_is_not_an_ai May 05 '15 at 14:59
  • 8
    Case 2 and 3 aren't 2D arrays, so this answer is misleading. [See this](http://stackoverflow.com/questions/30117161/why-do-i-need-to-use-type-to-point-to-type). – Lundin Jun 16 '15 at 08:05
  • @Zachary why can't I declare as you said? After using int (*a)[10] as parameter, I can't pass an array[10][10] into that function. – Jarkid Mar 27 '17 at 11:17
  • 1
    All of these solutions lose some or all of the size information. – François Andrieux Jun 13 '18 at 17:57
  • How can 528 people upvote such an answer? This is C++, not C. – A M Feb 24 '23 at 10:29
248

Fixed Size

1. Pass by reference

template <size_t rows, size_t cols>
void process_2d_array_template(int (&array)[rows][cols])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

In C++ passing the array by reference without losing the dimension information is probably the safest, since one needn't worry about the caller passing an incorrect dimension (compiler flags when mismatching). However, this isn't possible with dynamic (freestore) arrays; it works for automatic (usually stack-living) arrays only i.e. the dimensionality should be known at compile time.

2. Pass by pointer

void process_2d_array_pointer(int (*array)[5][10])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < 5; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << (*array)[i][j] << '\t';
        std::cout << std::endl;
    }    
}

The C equivalent of the previous method is passing the array by pointer. This should not be confused with passing by the array's decayed pointer type (3), which is the common, popular method, albeit less safe than this one but more flexible. Like (1), use this method when all the dimensions of the array is fixed and known at compile-time. Note that when calling the function the array's address should be passed process_2d_array_pointer(&a) and not the address of the first element by decay process_2d_array_pointer(a).

Variable Size

These are inherited from C but are less safe, the compiler has no way of checking, guaranteeing that the caller is passing the required dimensions. The function only banks on what the caller passes in as the dimension(s). These are more flexible than the above ones since arrays of different lengths can be passed to them invariably.

It is to be remembered that there's no such thing as passing an array directly to a function in C [while in C++ they can be passed as a reference (1)]; (2) is passing a pointer to the array and not the array itself. Always passing an array as-is becomes a pointer-copy operation which is facilitated by array's nature of decaying into a pointer.

3. Pass by (value) a pointer to the decayed type

// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

Although int array[][10] is allowed, I'd not recommend it over the above syntax since the above syntax makes it clear that the identifier array is a single pointer to an array of 10 integers, while this syntax looks like it's a 2D array but is the same pointer to an array of 10 integers. Here we know the number of elements in a single row (i.e. the column size, 10 here) but the number of rows is unknown and hence to be passed as an argument. In this case there's some safety since the compiler can flag when a pointer to an array with second dimension not equal to 10 is passed. The first dimension is the varying part and can be omitted. See here for the rationale on why only the first dimension is allowed to be omitted.

4. Pass by pointer to a pointer

// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

Again there's an alternative syntax of int *array[10] which is the same as int **array. In this syntax the [10] is ignored as it decays into a pointer thereby becoming int **array. Perhaps it is just a cue to the caller that the passed array should have at least 10 columns, even then row count is required. In any case the compiler doesn't flag for any length/size violations (it only checks if the type passed is a pointer to pointer), hence requiring both row and column counts as parameter makes sense here.

Note: (4) is the least safest option since it hardly has any type check and the most inconvenient. One cannot legitimately pass a 2D array to this function; C-FAQ condemns the usual workaround of doing int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10); as it may potentially lead to undefined behaviour due to array flattening. The right way of passing an array in this method brings us to the inconvenient part i.e. we need an additional (surrogate) array of pointers with each of its element pointing to the respective row of the actual, to-be-passed array; this surrogate is then passed to the function (see below); all this for getting the same job done as the above methods which are more safer, cleaner and perhaps faster.

Here's a driver program to test the above functions:

#include <iostream>

// copy above functions here

int main()
{
    int a[5][10] = { { } };
    process_2d_array_template(a);
    process_2d_array_pointer(&a);    // <-- notice the unusual usage of addressof (&) operator on an array
    process_2d_array(a, 5);
    // works since a's first dimension decays into a pointer thereby becoming int (*)[10]

    int *b[5];  // surrogate
    for (size_t i = 0; i < 5; ++i)
    {
        b[i] = a[i];
    }
    // another popular way to define b: here the 2D arrays dims may be non-const, runtime var
    // int **b = new int*[5];
    // for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
    process_pointer_2_pointer(b, 5, 10);
    // process_2d_array(b, 5);
    // doesn't work since b's first dimension decays into a pointer thereby becoming int**
}
Community
  • 1
  • 1
legends2k
  • 31,634
  • 25
  • 118
  • 222
  • What about passing dynamically allocated arrays to functions in C++? In C11 standard it can be done for statically and dynamically allocated arrays like that fn(int col,int row, int array[col][row]): http://stackoverflow.com/questions/16004668/c-allocating-a-matrix-in-a-function/27366086#27366086 I have made the question for this problem: http://stackoverflow.com/questions/27457076/is-it-a-similar-function-in-c11-as-in-c11-for-the-twodimensional-matrix – 42n4 Dec 13 '14 at 09:16
  • @42n4 Case 4 covers (for C++ as well) that. For dynamically allocated arrays, just the line inside the loop would change from `b[i] = a[i];` to, say, `b[i] = new int[10];`. One may also make `b` dynamically allocated `int **b = int *[5];` and it'll still work as-is. – legends2k Dec 15 '14 at 06:42
  • 1
    How does addressing `array[i][j]` work into the function in **4)**? Because it has received ptr to ptr and does not know the value of last dimension, which is necessary to perform a shift for correct addressing? – user1234567 Dec 16 '14 at 16:27
  • 2
    `array[i][j]` is just pointer arithmetic i.e. to the value of the pointer `array`, it'd add `i` and dereference the result as `int*`, to which it would add `j` and dereference that location, reading an `int`. So, no, it needn't know any dimension for this. But, that's the whole point! The compiler takes the programmer's word in faith and if the programmer was incorrect, undefined behaviour ensues. This is the reason I'd mentioned that case 4 is the least safest option. – legends2k Dec 17 '14 at 03:04
  • In such cases a struct may serve you well. – Xofo Nov 16 '18 at 18:17
  • You could also use: void process_reference_2_pointer(int *(*&array), size_t rows, size_t cols); – user2338150 Jan 19 '20 at 08:07
49

A modification to shengy's first suggestion, you can use templates to make the function accept a multi-dimensional array variable (instead of storing an array of pointers that have to be managed and deleted):

template <size_t size_x, size_t size_y>
void func(double (&arr)[size_x][size_y])
{
    printf("%p\n", &arr);
}

int main()
{
    double a1[10][10];
    double a2[5][5];

    printf("%p\n%p\n\n", &a1, &a2);
    func(a1);
    func(a2);

    return 0;
}

The print statements are there to show that the arrays are getting passed by reference (by displaying the variables' addresses)

Zrax
  • 1,641
  • 10
  • 15
  • 2
    You should use `%p` for printing a pointer, and even then, you must cast it to `void *`, else `printf()` invokes undefined behavior. Furthermore, you should not use the addressof (`&`) operator when calling the functions, since the functions expect an argument of type `double (*)[size_y]`, whereas you currently pass them `double (*)[10][10]` and `double (*)[5][5]`. –  Oct 20 '13 at 08:26
  • If you're using templates making both dimensions as template arguments is more appropriate and is better since low-level pointer access may be completely avoided. – legends2k Mar 24 '14 at 12:43
  • 6
    This only works if the size of the array is known at compile time. – John Doe Dec 27 '17 at 16:51
  • @Georg Code above in answer is exactly what I'd suggested. It works in GCC 6.3 - [online demo](https://godbolt.org/z/Y11N0_). Did you forget to make the parameter a reference? – legends2k Nov 29 '18 at 05:39
33

Surprised that no one mentioned this yet, but you can simply template on anything 2D supporting [][] semantics.

template <typename TwoD>
void myFunction(TwoD& myArray){
     myArray[x][y] = 5;
     etc...
}

// call with
double anArray[10][10];
myFunction(anArray);

It works with any 2D "array-like" datastructure, such as std::vector<std::vector<T>>, or a user defined type to maximize code reuse.

LemonPi
  • 1,026
  • 9
  • 22
  • 3
    This should be the right answer. It solves all problems mentioned and some that were not mentioned here. Type safety, compile time incompatibility of arrays, no pointer arithmetic, no type casting, no data copying. Works for C and C++. – OpalApps Dec 07 '18 at 16:10
  • 5
    Well, this works for C++; C doesn't support templates. Doing it in C would require macros. – Gunnar May 21 '19 at 21:22
  • 3
    This answer does not go far enough. It does not explain how to iterate over the elements of the 2D array. – R Sahu Apr 12 '21 at 16:47
  • How do you define the type `TwoD`? – VHS Sep 26 '22 at 03:56
  • 2
    @VHS It's a type template, so it's instantiated with any type you pass in (and that the compiler deduces). So you don't have to explicitly define TwoD. – LemonPi Sep 27 '22 at 04:03
  • This doesn't work for variable sized arrays – router Nov 29 '22 at 15:21
22

You can create a function template like this:

template<int R, int C>
void myFunction(double (&myArray)[R][C])
{
    myArray[x][y] = 5;
    etc...
}

Then you have both dimension sizes via R and C. A different function will be created for each array size, so if your function is large and you call it with a variety of different array sizes, this may be costly. You could use it as a wrapper over a function like this though:

void myFunction(double * arr, int R, int C)
{
    arr[x * C + y] = 5;
    etc...
}

It treats the array as one dimensional, and uses arithmetic to figure out the offsets of the indexes. In this case, you would define the template like this:

template<int C, int R>
void myFunction(double (&myArray)[R][C])
{
    myFunction(*myArray, R, C);
}
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
13

anArray[10][10] is not a pointer to a pointer, it is a contiguous chunk of memory suitable for storing 100 values of type double, which compiler knows how to address because you specified the dimensions. You need to pass it to a function as an array. You can omit the size of the initial dimension, as follows:

void f(double p[][10]) {
}

However, this will not let you pass arrays with the last dimension other than ten.

The best solution in C++ is to use std::vector<std::vector<double> >: it is nearly as efficient, and significantly more convenient.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
9

Here is a vector of vectors matrix example

#include <iostream>
#include <vector>
using namespace std;

typedef vector< vector<int> > Matrix;

void print(Matrix& m)
{
   int M=m.size();
   int N=m[0].size();
   for(int i=0; i<M; i++) {
      for(int j=0; j<N; j++)
         cout << m[i][j] << " ";
      cout << endl;
   }
   cout << endl;
}


int main()
{
    Matrix m = { {1,2,3,4},
                 {5,6,7,8},
                 {9,1,2,3} };
    print(m);

    //To initialize a 3 x 4 matrix with 0:
    Matrix n( 3,vector<int>(4,0));
    print(n);
    return 0;
}

output:

1 2 3 4
5 6 7 8
9 1 2 3

0 0 0 0
0 0 0 0
0 0 0 0
JayS
  • 2,057
  • 24
  • 16
8

We can use several ways to pass a 2D array to a function:

  • Using single pointer we have to typecast the 2D array.

     #include<bits/stdc++.h>
     using namespace std;
    
    
     void func(int *arr, int m, int n)
     {
         for (int i=0; i<m; i++)
         {
            for (int j=0; j<n; j++)
            {
               cout<<*((arr+i*n) + j)<<" ";
            }
            cout<<endl;
         }
     }
    
     int main()
     {
         int m = 3, n = 3;
         int arr[m][n] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
         func((int *)arr, m, n);
         return 0;
     }
    
  • Using double pointer In this way, we also typecast the 2d array

     #include<bits/stdc++.h>
     using namespace std;
    
    void func(int **arr, int row, int col)
    {
       for (int i=0; i<row; i++)
       {
          for(int j=0 ; j<col; j++)
          {
            cout<<arr[i][j]<<" ";
          }
          printf("\n");
       }
    }
    
    int main()
    {
      int row, colum;
      cin>>row>>colum;
      int** arr = new int*[row];
    
      for(int i=0; i<row; i++)
      {
         arr[i] = new int[colum];
      }
    
      for(int i=0; i<row; i++)
      {
          for(int j=0; j<colum; j++)
          {
             cin>>arr[i][j];
          }
      }
      func(arr, row, colum);
    
      return 0;
    }
    
ks1322
  • 33,961
  • 14
  • 109
  • 164
rashedcs
  • 3,588
  • 2
  • 39
  • 40
8

Single dimensional array decays to a pointer pointer pointing to the first element in the array. While a 2D array decays to a pointer pointing to first row. So, the function prototype should be -

void myFunction(double (*myArray) [10]);

I would prefer std::vector over raw arrays.

Mahesh
  • 34,573
  • 20
  • 89
  • 115
7

You can do something like this...

#include<iostream>

using namespace std;

//for changing values in 2D array
void myFunc(double *a,int rows,int cols){
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            *(a+ i*rows + j)+=10.0;
        }
    }
}

//for printing 2D array,similar to myFunc
void printArray(double *a,int rows,int cols){
    cout<<"Printing your array...\n";
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            cout<<*(a+ i*rows + j)<<"  ";
        }
    cout<<"\n";
    }
}

int main(){
    //declare and initialize your array
    double a[2][2]={{1.5 , 2.5},{3.5 , 4.5}};

    //the 1st argument is the address of the first row i.e
    //the first 1D array
    //the 2nd argument is the no of rows of your array
    //the 3rd argument is the no of columns of your array
    myFunc(a[0],2,2);

    //same way as myFunc
    printArray(a[0],2,2);

    return 0;
}

Your output will be as follows...

11.5  12.5
13.5  14.5
Sagar Shah
  • 391
  • 5
  • 11
  • 1
    The only reason I can come up with of why one would mangle the array in this case, is because one is lacking knowledge about how array pointers work. – Lundin Jun 16 '15 at 08:09
  • 5
    the i variable must be multiplied by columns, not by rows unless columns and rows are equal like in this case – Andrey Chernukha Jan 05 '16 at 21:51
  • 1
    * (a+ (i* cols) + j) is true. not this * (a+( i* rows) + j) . plz fix it – Sadegh J Dec 14 '20 at 13:35
  • Can't edit because suggested edit que is full, but as @Sadegh says, should be `* (a+ (i* cols) + j)` because you are skipping over the elements in that column to get to the next row. – James Newton Mar 25 '21 at 01:38
4

One important thing for passing multidimensional arrays is:

  • First array dimension need not be specified.
  • Second(any any further)dimension must be specified.

1.When only second dimension is available globally (either as a macro or as a global constant)

const int N = 3;

void print(int arr[][N], int m)
{
int i, j;
for (i = 0; i < m; i++)
  for (j = 0; j < N; j++)
    printf("%d ", arr[i][j]);
}

int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
print(arr, 3);
return 0;
}

2.Using a single pointer: In this method,we must typecast the 2D array when passing to function.

void print(int *arr, int m, int n)
{
int i, j;
for (i = 0; i < m; i++)
  for (j = 0; j < n; j++)
    printf("%d ", *((arr+i*n) + j));
 }

int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int m = 3, n = 3;

// We can also use "print(&arr[0][0], m, n);"
print((int *)arr, m, n);
return 0;
}
ks1322
  • 33,961
  • 14
  • 109
  • 164
sonorous
  • 71
  • 6
2
#include <iostream>

/**
 * Prints out the elements of a 2D array row by row.
 *
 * @param arr The 2D array whose elements will be printed.
 */
template <typename T, size_t rows, size_t cols>
void Print2DArray(T (&arr)[rows][cols]) {
    std::cout << '\n';
    for (size_t row = 0; row < rows; row++) {
        for (size_t col = 0; col < cols; col++) {
            std::cout << arr[row][col] << ' ';
        }
        std::cout << '\n';
    }    
}

int main()
{
    int i[2][5] = { {0, 1, 2, 3, 4},
                    {5, 6, 7, 8, 9} };
    char c[3][9] = { {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'},
                     {'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R'},
                     {'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '&'} };
    std::string s[4][4] = { {"Amelia", "Edward", "Israel", "Maddox"},
                            {"Brandi", "Fabian", "Jordan", "Norman"},
                            {"Carmen", "George", "Kelvin", "Oliver"},
                            {"Deanna", "Harvey", "Ludwig", "Philip"} };
    Print2DArray(i);
    Print2DArray(c);
    Print2DArray(s);
    std::cout <<'\n';
}
q-l-p
  • 4,304
  • 3
  • 16
  • 36
1

In the case you want to pass a dynamic sized 2-d array to a function, using some pointers could work for you.

void func1(int *arr, int n, int m){
    ...
    int i_j_the_element = arr[i * m + j];  // use the idiom of i * m + j for arr[i][j] 
    ...
}

void func2(){
    ...
    int arr[n][m];
    ...
    func1(&(arr[0][0]), n, m);
}
1

Despite appearances, the data structure implied by double** is fundamentally incompatible with that of a fixed c-array (double[][]). The problem is that both are popular (although) misguided ways to deal with arrays in C (or C++). See https://www.fftw.org/fftw3_doc/Dynamic-Arrays-in-C_002dThe-Wrong-Way.html

If you can't control either part of the code you need a translation layer (called adapt here), as explained here: https://c-faq.com/aryptr/dynmuldimary.html

You need to generate an auxiliary array of pointers, pointing to each row of the c-array.

#include<algorithm>
#include<cassert>
#include<vector>

void myFunction(double** myArray) {
    myArray[2][3] = 5;
}

template<std::size_t N, std::size_t M>
auto adapt(double(&Carr2D)[N][M]) {
    std::array<double*, N> ret;
    std::transform(
        std::begin(Carr2D), std::end(Carr2D),
        ret.begin(),
        [](auto&& row) { return &row[0];}
    );
    return ret;
}

int main() {
    double anArray[10][10];

    myFunction( adapt(anArray).data() );

    assert(anArray[2][3] == 5);
}

(see working code here: https://godbolt.org/z/7M7KPzbWY)

If it looks like a recipe for disaster is because it is, as I said the two data structures are fundamentally incompatible.


If you can control both ends of the code, these days, you are better off using a modern (or semimodern) array library, like Boost.MultiArray, Boost.uBLAS, Eigen or Multi. If the arrays are going to be small, you have "tiny" arrays libraries, for example inside Eigen or if you can't afford any dependency you might try simply with std::array<std::array<double, N>, M>.

With Multi, you can simply do this:

#include<multi/array.hpp>

#include<cassert>

namespace multi = boost::multi;

template<class Array2D>
void myFunction(Array2D&& myArray) {
    myArray[2][3] = 5;
}

int main() {
    multi::array<double, 2> anArray({10, 10});

    myFunction(anArray);

    assert(anArray[2][3] == 5);
}

(working code: https://godbolt.org/z/7M7KPzbWY)

alfC
  • 14,261
  • 4
  • 67
  • 118
0

You can use template facility in C++ to do this. I did something like this :

template<typename T, size_t col>
T process(T a[][col], size_t row) {
...
}

the problem with this approach is that for every value of col which you provide, the a new function definition is instantiated using the template. so,

int some_mat[3][3], another_mat[4,5];
process(some_mat, 3);
process(another_mat, 4);

instantiates the template twice to produce 2 function definitions (one where col = 3 and one where col = 5).

vantony
  • 513
  • 6
  • 9
0

If you want to pass int a[2][3] to void func(int** pp) you need auxiliary steps as follows.

int a[2][3];
int* p[2] = {a[0],a[1]};
int** pp = p;

func(pp);

As the first [2] can be implicitly specified, it can be simplified further as.

int a[][3];
int* p[] = {a[0],a[1]};
int** pp = p;

func(pp);
Second Person Shooter
  • 14,188
  • 21
  • 90
  • 165
0

You are allowed to omit the leftmost dimension and so you end up with two options:

void f1(double a[][2][3]) { ... }

void f2(double (*a)[2][3]) { ... }

double a[1][2][3];

f1(a); // ok
f2(a); // ok 

This is the same with pointers:

// compilation error: cannot convert ‘double (*)[2][3]’ to ‘double***’ 
// double ***p1 = a;

// compilation error: cannot convert ‘double (*)[2][3]’ to ‘double (**)[3]’
// double (**p2)[3] = a;

double (*p3)[2][3] = a; // ok

// compilation error: array of pointers != pointer to array
// double *p4[2][3] = a;

double (*p5)[3] = a[0]; // ok

double *p6 = a[0][1]; // ok

The decay of an N dimensional array to a pointer to N-1 dimensional array is allowed by C++ standard, since you can lose the leftmost dimension and still being able to correctly access array elements with N-1 dimension information.

Details in here

Though, arrays and pointers are not the same: an array can decay into a pointer, but a pointer doesn't carry state about the size/configuration of the data to which it points.

A char ** is a pointer to a memory block containing character pointers, which themselves point to memory blocks of characters. A char [][] is a single memory block which contains characters. This has an impact on how the compiler translate the code and how the final performance will be.

Source

luca
  • 7,178
  • 7
  • 41
  • 55
0

You could take arrays of an arbitrary number of dimensions by reference and peel off one layer at a time recursively.

Here's an example of a print function for demonstrational purposes:

#include <cstddef>
#include <iostream>
#include <iterator>
#include <string>
#include <type_traits>

template <class T, std::size_t N>
void print(const T (&arr)[N], unsigned indent = 0) {
    if constexpr (std::rank_v<T> == 0) {
        // inner layer - print the values:
        std::cout << std::string(indent, ' ') << '{';
        auto it = std::begin(arr);
        std::cout << *it;
        for (++it; it != std::end(arr); ++it) {
            std::cout << ", " << *it;
        }
        std::cout << '}';
    } else {
        // still more layers to peel off:
        std::cout << std::string(indent, ' ') << "{\n";
        auto it = std::begin(arr);
        print(*it, indent + 1);
        for (++it; it != std::end(arr); ++it) {
            std::cout << ",\n";
            print(*it, indent + 1);
        }
        std::cout << '\n' << std::string(indent, ' ') << '}';
    }
}

Here's a usage example with a 3 dimensional array:

int main() {
    int array[2][3][5]
    {
        {
            {1, 2, 9, -5, 3},
            {6, 7, 8, -45, -7},
            {11, 12, 13, 14, 25}
        },
        {
            {4, 5, 0, 33, 34},
            {8, 9, 99, 54, 44},
            {14, 15, 16, 19, 20}
        }
    };

    print(array);
}

... which will produce this output:

{
 {
  {1, 2, 9, -5, 3},
  {6, 7, 8, -45, -7},
  {11, 12, 13, 14, 25}
 },
 {
  {4, 5, 0, 33, 34},
  {8, 9, 99, 54, 44},
  {14, 15, 16, 19, 20}
 }
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
0

Updated answer for C++201. We now have std::span. It is the recommended way for functions to accept arrays of arbitrary length.

So far I have this:

void myFunction(double** myArray){

2D arrays are simply arrays of arrays.

That function can accept (a pointer to first element of) an array of pointers to double. It cannot accept (a pointer to first element of) an array of arrays.

Here is an example of using std::span with an array of arrays:

#include <span>

void myFunction(std::span<double[10]> table){
    for (auto& row : table) {
        for (auto& value : row){
            value = 42.;
        }
    }
}

// usage
double anArray[10][10];
myFunction(anArray);

So for example, [10][10] and [5][5]

Unfortunately, only the outermost dimension may be variable in the above example (as it is in the answers using int a[][10]). If the sizes can be known at compile time, then template is an easy solution, and those have already been covered well by the other answers.

legends2k suggests process_pointer_2_pointer which works with an array of pointers, and thus allows both dimensions to be determined at runtime(the rows may even have different sizes within the same array), but requires converting the 2D array. Although the approach isn't ideal in the first place, std::span can be used here as well:

void myFunction(std::span<std::span<double>> table){
    for (auto& row : table) {
        for (auto& value : row){
            value = 42.;
        }
    }
}

// usage
double anArray[10][10];

std::span<double> surrogate[10];
for (std::size_t i = 0; i < std::size(anArray); i++)
{
    surrogate[i] = anArray[i];
}
myFunction(surrogate);

alfC's answer has a handy adapt for doing the conversion. I'll leave it as an exercise to the reader to implement adapt that returns std::array of std::span.

I'll also want to second alfC's recommendation to use a proper abstraction - which you can find from a library - if the runtime variable dimensions are needed.


1 If you're stuck with C++11..17, you don't necessarily need to fall back to the old ways. span is implementable pre-C++20 and libraries implementing it exist.

eerorika
  • 232,697
  • 12
  • 197
  • 326