简体   繁体   中英

Passing array with unknown size to function

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?

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

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?

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

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 .

And as the comments to this answer state:

boost::multiarray may be appropriate since it more efficiently models a multidimensional array. A vector of vectors can have performance implications in critical path code, but in typical cases you will probably not notice an issue.

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] .

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.

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 (ie 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.

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

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.

  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.

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, eg 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, eg MyFunction( int * arr, size_t arrSize );

传递指针并自己进行索引或改为使用Matrix类。

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.

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

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

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

MyFunction(Board theBoard)
{
    ...
}

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!
    }
}

in reality my array's size is defined by two static const int s 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?

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM