Tuesday, December 22, 2009

C++ Defining Multi Dimensional Arrays with empty brackets []

An array with 1 dimension can be defined as
int matrixA[] = {1,2,3,4};

Say we want to define a 2 dimensional matrix. It seems logical to do:
int matrixA[][] = {
                   {1,2,3,4},
                   {5,6,7,8},
                   {1,3,5,7},
                   {2,4,6,8}
    };             
However this gives a compile error:
error: declaration of `matrixA' as multidimensional array must have bounds for all dimensions except the first

If we get rid of the inner curley brackets, like this, we still get the same error:
int matrixA[][] = { 1,2,3,4, 5,6,7,8, 1,3,5,7, 2,4,6,8 };

Note: the inner curley brackets did not mean anything to the compiler anyway, they just made the code more readible for humans.


Finally if we get rid of a square bracket in our defination, it will compile:
int matrixA[] = { 1,2,3,4, 5,6,7,8, 1,3,5,7, 2,4,6,8 };

However, we have now declared a 1 dimensional matrix and not a 2 dimensional matrix, which is not what we wanted.

But here is the clue as to what is going wrong. When we initialise an multi dimensional array like this:
int matrixA[4][4] = { 1,2,3,4, 5,6,7,8, 1,3,5,7, 2,4,6,8 };

and we access the last element by matrixA[3][3]


As the array is stored as one block of memory, this is equivalent to
int matrixA[16] = { 1,2,3,4, 5,6,7,8, 1,3,5,7, 2,4,6,8 };

and we access the last element by matrixA[15]

Since the compiler is just allocating 16 consequitive elements when we declare matrixA[4][4], it does not really know about the 4 rows and 4 columns in our matrix. Therefore when we do

int matrixA[] = { 1,2,3,4, 5,6,7,8, 1,3,5,7, 2,4,6,8 };


the compiler knows that this is the same as
int matrixA[16] = { 1,2,3,4, 5,6,7,8, 1,3,5,7, 2,4,6,8 };

But when we do
int matrixA[][] = { {1,2,3,4}, {5,6,7,8}, {1,3,5,7}, {2,4,6,8} };

which to the compiler is the same as
int matrixA[][] = { 1,2,3,4, 5,6,7,8, 1,3,5,7, 2,4,6,8 };

The compiler does not know that what we want is a table of 4 rows and 4 columns

However we can help the compiler by telling it that this is a 2 dimensional array where the number of elements in each row is 4:
int matrixB[][4] = { {1,2,3,4}, {5,6,7,8}, {1,3,5,7}, {2,4,6,8} };

or (when the inner brackets are removed)

int matrixB[][4] = { 1,2,3,4, 5,6,7,8, 1,3,5,7, 2,4,6,8 };



Since we discuseed that
int matrixA[4][4]
is the same as
int matrixA[16]

You might think we could try to cast a one dimenional matrix to a 2 dimensional matrix like this:
int matrixA[] = { 1,2,3,4, 5,6,7,8, 1,3,5,7, 2,4,6,8 };
    int matrixB[4][4];
    matrixB = matrixA;
But this gives us
error: incompatible types in assignment of `int[16]' to `int[4][4]'


Even using a reinterpret_cast does not help e.g.
typedef int** matrix_type;
int matrix3[4][4] = reinterpret_cast(matrixA);
gives us
error: invalid initializer

If we declare matrix3 this way:
typedef int** matrix_type;
int** matrix3 = reinterpret_cast(matrixA);

The code will compile, but I have not found any documentation that says this is legal and at best we can expect undefined behaviour.


Another behaviour you may find when working with multi dimensional arrays is that while these are valid:
int matrixA[4][4] = { {1,2,3,4}, {5,6,7,8}, {1,3,5,7}, {2,4,6,8} };
int matrixA[4][4] = {  1,2,3,4 ,  5,6,7,8 ,  1,3,5,7 ,  2,4,6,8  };

This is not valid
int matrixA[16] = { {1,2,3,4}, {5,6,7,8}, {1,3,5,7}, {2,4,6,8} };
gives error: brace-enclosed initializer used to initialize 'int'

Although the compiler ignores enclosed brackets shown above for the definition of matrixA[4][4], if you supply enclosed brackets, it expects you to define the array as matrixA[4][4] and does not accept matrixA[16].

No comments: