tags:

views:

403

answers:

7

This came up from this answer to a previous question of mine. Is it guaranteed for the compiler to treat array[4][4] the same as array[16]? For instance, would either of the below calls to api_func() be safe?

void api_func(const double matrix[4][4]);

// ...

{
  typedef double Matrix[4][4];

  double* array1 = new double[16];
  double array2[16];

  // ...

  api_func(reinterpret_cast<Matrix&>(array1));
  api_func(reinterpret_cast<Matrix&>(array2));
}
A: 

I would be worried about padding being added for things like Matrix[5][5] to make each row word aligned, but that could be simply my own superstition.

Josh Matthews
+2  A: 

I don't think there is a problem with padding introduced by having a multi-dimensional array.

Each element in an array must satisfy the padding requirements imposed by the architecture. An array [N][M] is always going to have the same in memory representation as one of [M*N].

Rob Walker
+1  A: 

Each array element should be laid out sequentially in memory by the compiler. The two declarations whilst different types are the same underlying memory structure.

Henk
+3  A: 

From the C++ standard, referring to the sizeof operator:

When applied to an array, the result is the total number of bytes in the array. This implies that the size of an array of n elements is n times the size of an element.

From this, I'd say that double[4][4] and double[16] would have to have the same underlying representation.

I.e., given

sizeof(double[4]) = 4*sizeof(double)

and

sizeof(double[4][4]) = 4*sizeof(double[4])

then we have

sizeof(double[4][4]) = 4*4*sizeof(double) = 16*sizeof(double) = sizeof(double[16])

I think a standards-compliant compiler would have to implement these the same, and I think that this isn't something that a compiler would accidentally break. The standard way of implementing multi-dimensional arrays works as expected. Breaking the standard would require extra work, for likely no benefit.

The C++ standard also states that an array consists of contiguously-allocated elements, which eliminates the possibility of doing anything strange using pointers and padding.

Derek Park
A: 

A bigger question is: do you really need to perform such a cast?

Although you might be able to get away with it, it would still be more readable and maintainable to avoid altogether. For example, you could consistently use double[m*n] as the actual type, and then work with a class that wraps this type, and perhaps overloads the [] operator for ease of use. In that case, you might also need an intermediate class to encapsulate a single row -- so that code like my_matrix[3][5] still works as expected.

Tyler
+1  A: 

@Konrad Rudolph:

I get those two (row major/column major) mixed up myself, but I do know this: It's well-defined.

int x[3][5], for example, is an array of size 3, whose elements are int arrays of size 5. (§6.5.2.1) Adding all the rules from the standard about arrays, addressing, etc. you get that the second subscript references consecutive integers, wheras the first subscript will reference consecutive 5-int objects. (So 3 is the bigger number; you have 5 ints between x[1][0] and x[2][0].)

aib
I stand corrected.
Konrad Rudolph
A: 

@Tyler: I agree this sort of cast would be bad form. I'm assuming the api_func() in question is something fixed, and the cast would be a way to mould other data (that's in a slightly different form for whatever reason) to the required shape, instead of having to copy all array values into a new structure before passing them to the function.

Owen