tags:

views:

289

answers:

17

Let's say I have a function called MyFunction(int myArray[][]) that does some array manipulations.

If I write the parameter list like that, the compiler will complain that it needs to know the size of the array at compile time. Is there a way to rewrite the parameter list so that I can pass an array with any size to the function?

Edit: in reality my array's size is defined by two static const ints in a class, but the compiler won't accept something like MyFunction(int myArray[Board::ROWS][Board::COLS]).

Edit: what if I could convert the array to a vector and then pass the vector to MyFunction? Is there a one-line conversion that I can use or do I have to do the conversion manually?

+1  A: 

Yes: MyFunction(int **myArray);

Careful, though. You'd better know what you're doing. This will only accept an array of int pointers.

Since you're trying to pass an array of arrays, you'll need a constant expression as one of the dimentions:

MyFunction(int myArray[][COLS]);

You'll need to have COLS at compile time.

I suggest using a vector instead.

JoshD
This is not equivalent to what the user wants: An bidimensional array of ints cannot passes as a pointer to pointer to int.
David Rodríguez - dribeas
A: 

Pass a pointer and do the indexing yourself or use a Matrix class instead.

src
+7  A: 

In C++ use std::vector to model arrays unless you have a specific reason for using an array.

Example of a 3x2 vector filled with 0's called "myArray" being initialized:

vector< vector<int> > myArray(3, vector<int>(2,0));

Passing this construct around is trivial, and you don't need to screw around with passing length (because it keeps track):

void myFunction(vector< vector<int> > &myArray) {
    for(size_t x = 0;x < myArray.length();++x){
        for(size_t y = 0;y < myArray[x].length();++y){
            cout << myArray[x][y] << " ";
        }
        cout << endl;
    }
}

Alternatively you can iterate over it with iterators:

void myFunction(vector< vector<int> > &myArray) {
    for(vector< vector<int> >::iterator x = myArray.begin();x != myArray.end();++x){
        for(vector<int>::iterator y = x->begin();y != x->end();++y){
            cout << *y << " ";
        }
        cout << endl;
    }
}

In C++0x you can use the auto keyword to clean up the vector iterator solution:

void myFunction(vector< vector<int> > &myArray) {
    for(auto x = myArray.begin();x != myArray.end();++x){
        for(auto y = x->begin();y != x->end();++y){
            cout << *y << " ";
        }
        cout << endl;
    }
}

And in c++0x for_each becomes viable with lambdas

void myFunction(vector< vector<int> > &myArray) {
    for_each(myArray.begin(), myArray.end(), [](const vector<int> &x){
        for_each(x->begin(), x->end(), [](int value){
            cout << value << " ";
        });
        cout << endl;
    });
}

Or a range based for loop in c++0x:

void myFunction(vector< vector<int> > &myArray) {
    for(auto x : myArray){
        for(auto y : *x){
            cout << *y << " ";
        }
        cout << endl;
    }
}

*I am not near a compiler right now and have not tested these, please feel free to correct my examples.


If you know the size of the array at compile time you can do the following (assuming the size is [x][10]):

MyFunction(int myArray[][10])

If you need to pass in a variable length array (dynamically allocated or possibly just a function which needs to take different sizes of arrays) then you need to deal with pointers.

M2tM
+1 - great answer
Kiril Kirov
or `std::array` and `boost::array`
Inverse
or `boost::multiarray` since this is supposed to be a 2D array :)
jalf
+1  A: 
  1. Use a vector<vector<int> > (this would be cheating if underlying storage was not guaranteed to be contiguous).

  2. Use a pointer to element-of-array (int*) and a size (M*N) parameter. Here be dragons.

Steve Townsend
+4  A: 

Pass it as a pointer, and take the dimension(s) as an argument.

void foo(int *array, int width, int height) {
    // initialize xPos and yPos
    assert(xPos >= 0 && xPos < width);
    assert(yPos >= 0 && yPos < height);
    int value = array[yPos * width + xPos];
}

This is assuming you have a simple two-dimensional array, like int x[50][50].

EboMike
Oli Charlesworth
A: 

Use MyFunction(int *myArray[])
If you use MyFunction(int **myArray) an pass int someArray[X][Y], the program will crash.
EDIT: Don't use the first line, it's explained in comments.

Dadam
No, it won't even compile!
Oli Charlesworth
You are right, my mistake. First line would work for 2D arrays defined as array of pointers pointing on another arrays.
Dadam
So would the second line (they're exactly equivalent).
Oli Charlesworth
A: 

yes - just pass it as pointer(s):

MyFunction(int** someArray)

The downside is that you'll probably need to pas the array's lengths as well

Dror Helper
This isn't as easy as it sounds. The questioner presumably has something like `int a[M][N]`, and wants to pass `a` to his function. To get working double-dereferencing, he'll have to create an array of pointers to rows first.
Oli Charlesworth
Actually once inside the method he can use a[x][y] to get the correct item
Dror Helper
+2  A: 

You can't pass an arbitrary size like that; the compiler doesn't know how to generate the pointer arithmetic. You could do something like:

MyFunction(int myArray[][N])

or you could do:

MyFunction(int *p, int M, int N)

but you'll have to take the address of the first element when you call it (i.e. MyFunction(&arr[0][0], M, N).

You can get round all of these problems in C++ by using a container class; std::vector would be a good place to start.

Oli Charlesworth
+2  A: 

The compiler is complaining because it needs to know the size of the all but the first dimension to be able to address an element in the array. For instance, in the following code:

int array[M][N];
// ...
array[i][j] = 0;

To address the element, the compiler generates something like the following:

*(array+(i*N+j)) = 0;

Therefore, you need to re-write your signature like this:

MyFunction(int array[][N])

in which case you will be stuck with a fixed dimension, or go with a more general solution such as a (custom) dynamic 2D array class or a vector<vector<int> >.

André Caron
+8  A: 

In C++ language, multidimensional array declarations must always include all sizes except possibly the first one. So, what you are trying to do is not possible. You cannot declare a parameter of built-in multidimensional array type without explicitly specifying the sizes.

If you need to pass a run-time sized multidimensional array to a function, you can forget about using built-in multidimensional array type. One possible workaround here is to use a "simulated" multidimensional array (1D array of pointers to other 1D arrays; or a plain 1D array that simulates multidimensional array through index recalculation).

AndreyT
+1 for the clearest explanation for the q as it was asked
Steve Townsend
It lacks any mention of vector, however, this answer is appropriate for the question asked, but when instructing beginners (this is a beginner question) it's better to steer them towards the actual answer to their problem instead of simply taking what they ask at face value.
M2tM
@M2tmM: for multidimensional arrays, vectors often aren't such a hot solution.
jalf
M2tM
I will say that a two dimensional vector is probably just fine (and standard) in most cases.
M2tM
+1  A: 

First, lets see why compiler is complaining.

If an array is defined as int arr[ ROWS ][ COLS ]; then any array notation arr[ i ][ j ] can be translated to pointer notation as

*( arr + i * COLS + j )

Observe that the expression requires only COLS, it does not require ROWS. So, the array definition can be written equivalently as

int arr [][ COLS ];

But, missing the second dimension is not acceptable. For little more details, read here.

Now, on your question:

Is there a way to rewrite the parameter list so that I can pass an array with any size to the function?

Yes, perhaps you can use a pointer, e.g. MyFunction( int * arr );. But, think about it, how would MyFunction() know where to stop accessing the array? To solve that you would need another parameter for the length of the array, e.g. MyFunction( int * arr, size_t arrSize );

ArunSaha
A: 

I don't know about C++, but the C99 standard introduced variable length arrays.

So this would work in a compiler that supports C99:

void func(int rows, int cols, double[rows][cols] matrix) {
    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < cols; c++) {
            printf("%f", matrix[r][c]);
        }
    }
}

Note that the size arguments come before the array. Really, only the number of columns has to be known at compile time, so this would be valid as well:

void func(int rows, int cols, double[][cols] matrix)

For three or more dimensions, all but the first dimension must have known sizes. The answer ArunSaha linked to explains why.

Honestly, I don't know whether C++ supports variable-length arrays, so this may or may not work. In either case, you may also consider encapsulating your array in some sort of matrix class.

EDIT: From your edit, it looks like C++ may not support this feature. A matrix class is probably the way to go. (Or std::vector if you don't mind that the memory may not be allocated contiguously.)

ajduff574
There is no support in C++ for variable length arrays. The closest that you can do is at compile time by means of templates, having the compiler deduce the sizes.
David Rodríguez - dribeas
You're right, it looks like some C99 changes will be added to C++0x, but not VLAs: http://www2.research.att.com/~bs/C++0xFAQ.html#C99
ajduff574
A: 

In C++, using the inbuilt array types is instant fail. You could use a boost::/std:: array of arrays or vector of arrays. Primitive arrays are not up to any sort of real use

DeadMG
+1  A: 

There are already a set of answers with the most of the common suggestions: using std::vector, implementing a matrix class, providing the size of the array in the function argument... I am only going to add yet another solution based on native arrays --note that if possible you should use a higher level abstraction.

At any rate:

template <std::size_t rows, std::size_t cols>
void function( int (&array)[rows][cols] )
{
   // ...
}

This solution uses a reference to the array (note the & and the set of parenthesis around array) instead of using the pass-by-value syntax. This forces the compiler not to decay the array into a pointer. Then the two sizes (which could have been provided as compile time constants can be defined as template arguments and the compiler will deduct the sizes for you.

NOTE: You mention in the question that the sizes are actually static constants you should be able to use them in the function signature if you provide the value in the class declaration:

struct test {
   static const int rows = 25;
   static const int cols = 80;
};
void function( int *array[80], int rows ) {
   // ...
}

Notice that in the signature I prefer to change the double dimension array for a pointer to an array. The reason is that this is what the compiler interprets either way, and this way it is clear that there is no guarantee that the caller of the function will pass an array of exactly 25 lines (the compiler will not enforce it), and it is thus apparent the need for the second integer argument where the caller passes the number of rows.

David Rodríguez - dribeas
A: 

Don't pass an array, which is an implementation detail. Pass the Board

MyFunction(Board theBoard)
{
    ...
}
Stephen P
A: 

In C++0x, you can use std::initializer_list<...> to accomplish this:

MyFunction(std::initializer_list<std::initializer_list<int>> myArray);

and use it (I presume) like this (with the range based for syntax):

for (const std::initializer_list<int> &subArray: myArray)
{
    for (int value: subArray)
    {
        // fun with value!
    }
}
MSN
A: 

in reality my array's size is defined by two static const ints in a class, but the compiler won't accept something like MyFunction(int myArray[Board::ROWS][Board::COLS]).

That's strange, it works perfectly fine for me:

struct Board
{
    static const int ROWS = 6;
    static const int COLS = 7;
};

void MyFunction(int myArray[Board::ROWS][Board::COLS])
{
}

Maybe ROWS and COLS are private? Can you show us some code?

FredOverflow