views:

583

answers:

4

when declaring the two dimensional array

int random[height][width];

and then using it in a function

void populate(int random[height][width], int x, int y)

gives the error variable-size type declared outside of any function. I know I'm doing something wrong, and that its something small. I just have a bad memory...

+1  A: 

You can not define the array with non-constant dimensions (width, height) outside of the function, that is - not in the stack frame, because the dimensions aren't known at compile-time. You'd have use constants or allocate it dynamically (either in heap or in the stack frame).

Michael Krelin - hacker
+3  A: 

I'm going to step up right now and tell you that multidimensional arrays are not worth the brain effort in C or C++. You're much better off using single-dimensional arrays (or, better yet, standard containers) and writing an indexing function:

inline int index (int x, int y)
{
  return x + y * width;
}

Now for your problem. C++ does not support C99 variable-length arrays. The compiler must know, at compile time, the size of the array. The following, for example, won't work.

int dim = 4;
int ar[dim];

If dim were const, it would work because the compiler would be able to tell exactly how wide ar should be (because the value of dim wouldn't change). This is probably the problem you're encountering.

If you want to be able to change the size at compile-time, you'll need to do something more difficult, like write a templated reference. You can't use a pointer for multidimensional arrays because of the way they are laid out in C/C++. A templated example might look like the following aberration:

template <int Width, int Height>
void populate(int (&(&random)[Width])[Height], int x, int y);

This is ugly.

For run-time, you'll need to use new to allocate data, or use a container type.

coppro
I have since changed the dimensions of the array to constants, and I still get the error.
Mike
Mike, I think it would be more helpful if you could post the minimal code that errors out compiler. Because `int random[25][80]` looks perfectly legal.
Michael Krelin - hacker
coppro, how is your template helping to change dimensions *at run time*? ;-)
Michael Krelin - hacker
I can post the code of the entire program if you'd like.
Mike
Mike, please, make the entire program short then ;-)
Michael Krelin - hacker
your template has a reference-to-array-of-array-references as parameter. that's illegal in c++.
Johannes Schaub - litb
All the more reason not to use it :P
coppro
A: 

You can use something like this:

void populate(int height, int width, int **random)
{
    //here you can work from random[0][0] to random[height][width]
}

then you can use it like this:

int main()
{
    int height=10;
    int width=20;
    int **myarray = new int*[height];
    for( int i=0; i< height; i++ ) myarray[i] = new int[width];
    populate( height, width, myarray);
}

but, of course, you will have to watch out for buffer overflows

Vargas
No, you can't pass a multidimentsional array as a `**`. You've got to have an array of pointers for this purpose.
Michael Krelin - hacker
That's correct, edited my post...
Vargas
+1  A: 

When an array is passed directly as parameter to a function (pass by value) it decays into a pointer to the first element of the array. Even if you can clearly read in the signature the dimensions of the array, those dimensions are ignored by the compiler. That behavior is compatible with C.

Using C++ you can pass the array by reference and that would not be a problem any more.

int extract_value( int (&a)[10][10], int row, int col ) {
   return a[row][col];
}
int main() {
   int a[10][10] = {};
   a[5][5] = 1;
   std::cout << extract_value( a, 5, 5 ) << std::endl;
   int b[5][5];
//   extract_value( b, 2, 2 ); // error: the function takes an array of 10x10
}

The function parameter must match exactly, that is, it only takes an array of 10x10 elements. You can take rid of that restriction by templating the function on the array sizes. Once you are at it also the type:

template <typename T, int Rows, int Cols>
T extract_value( T (&a)[Rows][Cols], int row, int col ) {
   return a[row][col];
}
int main() {
   int a[5][7] = {};
   extract_value( a, 3, 4 );
   int b[8][2] = {};
   extract_value( b, 7, 1 ); // correct, the compiler matches sizes
   double c[4][4] = {};
   extract_value( c, 2, 2 ); // different types are allowed
}

This solution is still cumbersome in that the sizes must be compile time constants, and the array must be stack allocated. The solution to this is defining some class that takes dynamic memory in a buffer (linear) and has a conversion from the N-coordinate system into the 1-dimensional array to obtain values, as it was suggested before. You can get some hints on how to do it in this FAQ about operator overloading that provides an implementation of a 2D matrix. Once you have that implemented, you can just use that as parameter to functions/methods.

My recommendation would be to follow this last path: encapsulate the N-dimensional array into a class that provides conversions into a 1D vector (the C++FAQ lite uses a raw pointer, I prefer STL containers).

David Rodríguez - dribeas