C / C++ MultiDimensional Array Internals - c

I have a question about how C / C++ internally stores multidimensional arrays declared using the notation foo[m][n]. I am not questioning pure pointers to pointers etc... I am asking because of speed reasons...
Correct me if I am wrong, but syntactically foo is an array of pointers, which themselves point to an array
int foo[5][4]
*(foo + i) // returns a memory address
*( *(foo + i) + j) // returns an int
I have heard from many places that the C/C++ compiler converts foo[m][n] to a one dimensional array behind the scenes (calculating the required one dimension index with i * width + j). However if this was true then the following would hold
*(foo + 1) // should return element foo[0][1]
Thus my question:
Is it true that foo[m][n] is (always?) stored in memory as a flat one dimensional array?? If so, why does the above code work as shown.

A two-dimensional array:
int foo[5][4];
is nothing more or less than an array of arrays:
typedef int row[4]; /* type "row" is an array of 4 ints */
row foo[5]; /* the object "foo" is an array of 5 rows */
There are no pointer objects here, either explicit or implicit.
Arrays are not pointers. Pointers are not arrays.
What often causes confusion is that an array expression is, in most contexts, implicitly converted to a pointer to its first element. (And a separate rule says that what looks like an array parameter declaration is really a pointer declaration, but that doesn't apply in this example.) An array object is an array object; declaring such an object does not create any pointer objects. Referring to an array object can create a pointer value (the address of the array's first element), but there is no pointer object stored in memory.
The array object foo is stored in memory as 5 contiguous elements, where each element is itself an array of 4 contiguous int elements; the whole thing is therefore stored as 20 contiguous int objects.
The indexing operator is defined in terms of pointer arithmetic; x[y] is equivalent to *(x + y). Typically the left operand is going to be either a pointer expression or an array expression; if it's an array expression, the array is implicitly converted to a pointer.
So foo[x][y] is equivalent to *(foo[x] + y), which in turn is equivalent to *(*(foo + x) + y). (Note that no casts are necessary.) Fortunately, you don't have to write it that way, and foo[x][y] is a lot easier to understand.
Note that you can create a data structure that can be accessed with the same foo[x][y] syntax, but where foo really is a pointer to pointer to int. (In that case, the prefix of each [] operator is already a pointer expression, and doesn't need to be converted.) But to do that, you'd have to declare foo as a pointer-to-pointer-to-int:
int **foo;
and then allocate and initialize all the necessary memory. This is more flexible than int foo[5][4], since you can determine the number of rows and the size (or even existence) of each row dynamically.
Section 6 of the comp.lang.c FAQ explains this very well.
EDIT:
In response to Arrakis's comment, it's important to keep in mind the distinction between type and representation.
For example, these two types:
struct pair { int x; int y;};
typedef int arr2[2];
very likely have the same representation in memory (two consecutive int objects), but the syntax to access the elements is quite different.
Similarly, the types int[5][4] and int[20] have the same memory layout (20 consecutive int objects), but the syntax to access the elements is different.
You can access foo[2][2] as ((int*)foo)[10] (treating the 2-dimensional array as if it were a 1-dimensional array). And sometimes it's useful to do so, but strictly speaking the behavior is undefined. You can likely get away with it because most C implementations don't do array bounds-checking. On the other hand, optimizing compilers can assume that your code's behavior is defined, and generate arbitrary code if it isn't.

Yes, C/C++ stores a multi-dimensional (rectangular) array as a contiguous memory area. But, your syntax is incorrect. To modify element foo[0][1], the following code will work:
*((int *)foo+1)=5;
The explicit cast is necessary, because foo+1, is the same as &foo[1] which is not at all the same thing as foo[0][1]. *(foo+1) is a pointer to the fifth element in the flat memory area. In other words, *(foo+1) is basically foo[1] and **(foo+1) is foo[1][0]. Here is how the memory is laid out for some of your two dimensional array:

C arrays - even multi-dimensional ones - are contiguous, ie an array of type int [4][5] is structurally equivalent to an array of type int [20].
However, these types are still incompatible according to C language semantics. In particular, the following code is in violation of the C standard:
int foo[4][5] = { { 0 } };
int *p = &foo[0][0];
int x = p[12]; // undefined behaviour - can't treat foo as int [20]
The reason for this is that the C standard is (probably intentionally) worded in a way which makes bounds-checking implementations possible: As p is derived from foo[0], which has type int [5], valid indices must be in range 0..5 (resp. 0..4 if you actually access the element).
Many other programming languages (Java, Perl, Python, JavaScript, ...) use jagged arrays to implement multi-dimensional arrays. This is also possible in C by using an array of pointers:
int *bar[4] = { NULL };
bar[0] = (int [3]){ 0 };
bar[1] = (int [5]){ 1, 2, 3, 4 };
int y = bar[1][2]; // y == 3
However, jagged arrays are not contiguous, and the pointed-to arrays need not be of uniform size.
Because of implicit conversion of array expressions into pointer expressions, indexing jagged and non-jagged arrays looks identical, but the actual address calculations will be quite different:
&foo[1] == (int (*)[5])((char *)&foo + 1 * sizeof (int [5]))
&bar[1] == (int **)((char *)&bar + 1 * sizeof (int *))
&foo[1][2] == (int *)((char *)&foo[1] + 2 * sizeof (int))
== (int *)((char *)&foo + 1 * sizeof (int [5]) + 2 * sizeof (int))
&bar[1][2] == (int *)((char *)bar[1] + 2 * sizeof (int)) // no & before bar!
== (int *)((char *)*(int **)((char *)&bar + 1 * sizeof (int *))
+ 2 * sizeof (int))

int foo[5][4];
foo is not an array of pointers; it's an array of arrays. Below image will help.

Related

Allocate 6xNxN array

I have a variable N. I need a 6xNxN array.
Something like this:
int arr[6][N][N];
But, obviously, that doesn't work.
I'm not sure how I'd go about allocating this so that I can access, e.g. arr[5][4][4] if N is 5, and arr[5][23][23] if N is 24.
Note that N will never change, so I'll never have to reallocate arr.
What should I do? Will int ***arr = malloc(6 * N * N * sizeof(int)); work?
You can allocate your 3-dimensional array on the heap as
int (*arr)[N][N] = malloc(sizeof(int[6][N][N]));
After use, you can free as
free(arr);
Another way of writing the same as suggested by #StoryTeller is -
int (*arr)[N][N] = malloc(6u * sizeof(*arr));
But here you need to be careful about the u after 6 to prevent signed arithmetic overflow.
Also, there can still be issues on platforms where size_t is smaller in width that int as suggested by #chqrlie, but that won't be the case on "most" commonly used platforms and hence you are fine using it.
int arr[6][N][N]; will work just fine. You merely need to update your compiler and C knowledge to the year 1999 or later, when variable-length arrays (VLA) were introduced to the language.
(If you have an older version of GCC than 5.0, you must explicitly tell it to not use an ancient version of the C standard, by passing -std=c99 or -std=c11.)
Alternatively if you need heap allocation, you can do:
int (*arrptr)[Y][Z] = malloc( sizeof(int[X][Y][Z]) );
You cannot do int ***arr = malloc(6 * N * N * sizeof(int)); since a int*** cannot point at a 3D array. In general, more than two levels of indirection is a certain sign that your program design is completely flawed.
Detailed info here: Correctly allocating multi-dimensional arrays.
What you want can't work directly. For indexing a multi-dimensional array, all but the very first dimension need to be part of the type and here's why:
The indexing operator operates on pointers by first adding an index to the pointer and then dereferencing it. The identifier of an array evaluates to a pointer to its first element (except when e.g. used with sizeof, _Alignof and &), so indexing on arrays works as you would expect.
It's very simple in the case of a single-dimension array. With
int a[42];
a evaluates to a pointer of type int * and indexing works the following way: a[18] => *(a + 18).
Now in a 2-dimensional array, all the elements are stored contiguously ("row" after "row" if you want to understand it as a matrix), and what's making the indexing "magic" work is the types involved. Take for example:
int a[16][42];
Here, the elements of a have the type int ()[42] (42-element array of int). According to the rules above, evaluating an expression of this type in most contexts again yields an int * pointer. But what about a itself? Well, it's an array of int ()[42] so a will evaluate to a pointer to 42-element array of int: int (*)[42]. Then let's have a look at what the indexing operator does:
a[3][18] => *(*(a + 3) + 18)
With a evaluating to the address of a with type int (*)[42], this inner addition of 3 can properly add 42 * sizeof(int). This would be impossible if the second dimension wasn't known in the type.
I guess it's simple to deduce the example for the n-dimensional case.
In your case, you have two possibilities to achieve something similar to what you want.
Use a dynamically allocated flat array with size 6*N*N. You can calculate the indices yourself if you save N somewhere.
Somewhat less efficient, but yielding better readable code, you could use an array of pointers to arrays of pointers to int (multiple indirection). You could e.g. do
int ***a = malloc(6 * sizeof *int);
for (size_t i = 0; i < 6; ++i)
{
a[i] = malloc(N * sizeof *(a[i]));
for (size_t j = 0; j < N ++j)
{
a[i][j] = malloc(N* sizeof *(a[i][j]));
}
}
// add error checking to malloc calls!
Then your accesses will look just like those to a normal 3d array, but it's stored internally as many arrays with pointers to the other arrays instead of in a big contiguous block.
I don't think it's worth using this many indirections, just to avoid writing e.g. a[2*N*N+5*N+4] to access the element at 2,5,4, so my recommendation would be the first method.
Making a simple change to the declaration on this line and keeping the malloc can easily solve your problem.
int ***arr = malloc(6 * N * N * sizeof(int));
However, int *** is unnecessary (and wrong). Use a flat array, which is easy to allocate:
int *flatarr = malloc(6 * N * N * sizeof(int));
This works for three dimensions, and instead of accessing arr[X][Y][Z] as in the question, you access flatarr[(X*N*N) + (Y*N) + Z]. In fact, you could even write a handy macro:
#define arr(X,Y,Z) flatarr[((X)*N*N) + ((Y)*N) + (Z)]
This is basically what I've done in my language Cubically to allow for multiple-size cubes. Thanks to Programming Puzzles & Code Golf user Dennis for giving me this idea.

C - Pass variable length array of strings to function and modify strings inside function [duplicate]

This question already has answers here:
Why can't we use double pointer to represent two dimensional arrays?
(6 answers)
Closed 6 years ago.
int main()
{
matrix[2][4] = {{11,22,33,99},{44,55,66,110}};
int **ptr = (int**)matrix;
printf("%d%d",**matrix,*ptr);
}
But when a 2-d array is passed as a parameter it is typecasted into (*matrix)[2] ..
what type does the compiler store this array as... is it storing as a 2-d array or a double pointer or an pointer to an array .. If it is storing as an array how does it interprets differently at different situations like above. Please help me understand.
Is 2d array a double pointer?
No. This line of your program is incorrect:
int **ptr = (int**)matrix;
This answer deals with the same topic
If you want concrete image how multidimensional arrays are implemented:
The rules for multidimensional arrays are not different from those for ordinary arrays, just substitute the "inner" array type as element type. The array items are stored in memory directly after each other:
matrix: 11 22 33 99 44 55 66 110
----------- the first element of matrix
------------ the second element of matrix
Therefore, to address element matrix[x][y], you take the base address of matrix + x*4 + y (4 is the inner array size).
When arrays are passed to functions, they decay to pointers to their first element. As you noticed, this would be int (*)[4]. The 4 in the type would then tell the compiler the size of the inner type, which is why it works. When doing pointer arithmetic on a similar pointer, the compiler adds multiples of the element size, so for matrix_ptr[x][y], you get matrix_ptr + x*4 + y, which is exactly the same as above.
The cast ptr=(int**)matrix is therefore incorrect. For once, *ptr would mean a pointer value stored at address of matrix, but there isn't any. Secondly, There isn't a pointer to matrix[1] anywhere in the memory of the program.
Note: the calculations in this post assume sizeof(int)==1, to avoid unnecessary complexity.
No. A multidimensional array is a single block of memory. The size of the block is the product of the dimensions multiplied by the size of the type of the elements, and indexing in each pair of brackets offsets into the array by the product of the dimensions for the remaining dimensions. So..
int arr[5][3][2];
is an array that holds 30 ints. arr[0][0][0] gives the first, arr[1][0][0] gives the seventh (offsets by 3 * 2). arr[0][1][0] gives the third (offsets by 2).
The pointers the array decays to will depend on the level; arr decays to a pointer to a 3x2 int array, arr[0] decays to a pointer to a 2 element int array, and arr[0][0] decays to a pointer to int.
However, you can also have an array of pointers, and treat it as a multidimensional array -- but it requires some extra setup, because you have to set each pointer to its array. Additionally, you lose the information about the sizes of the arrays within the array (sizeof would give the size of the pointer). On the other hand, you gain the ability to have differently sized sub-arrays and to change where the pointers point, which is useful if they need to be resized or rearranged. An array of pointers like this can be indexed like a multidimensional array, even though it's allocated and arranged differently and sizeof won't always behave the same way with it. A statically allocated example of this setup would be:
int *arr[3];
int aa[2] = { 10, 11 },
ab[2] = { 12, 13 },
ac[2] = { 14, 15 };
arr[0] = aa;
arr[1] = ab;
arr[2] = ac;
After the above, arr[1][0] is 12. But instead of giving the int found at 1 * 2 * sizeof(int) bytes past the start address of the array arr, it gives the int found at 0 * sizeof(int) bytes past the address pointed to by arr[1]. Also, sizeof(arr[0]) is equivalent to sizeof(int *) instead of sizeof(int) * 2.
In C, there's nothing special you need to know to understand multi-dimensional arrays. They work exactly the same way as if they were never specifically mentioned. All you need to know is that you can create an array of any type, including an array.
So when you see:
int matrix[2][4];
Just think, "matrix is an array of 2 things -- those things are arrays of 4 integers". All the normal rules for arrays apply. For example, matrix can easily decay into a pointer to its first member, just like any other array, which in this case is an array of four integers. (Which can, of course, itself decay.)
If you can use the stack for that data (small volume) then you usually define the matrix:
int matrix[X][Y]
When you want to allocate it in the heap (large volume), the you usually define a:
int** matrix = NULL;
and then allocate the two dimensions with malloc/calloc.
You can treat the 2d array as int** but that is not a good practice since it makes the code less readable. Other then that
**matrix == matrix[0][0] is true

Can't use fixed array in 'dynamic array' in struct [duplicate]

This question already has answers here:
Why can't we use double pointer to represent two dimensional arrays?
(6 answers)
Closed 6 years ago.
int main()
{
matrix[2][4] = {{11,22,33,99},{44,55,66,110}};
int **ptr = (int**)matrix;
printf("%d%d",**matrix,*ptr);
}
But when a 2-d array is passed as a parameter it is typecasted into (*matrix)[2] ..
what type does the compiler store this array as... is it storing as a 2-d array or a double pointer or an pointer to an array .. If it is storing as an array how does it interprets differently at different situations like above. Please help me understand.
Is 2d array a double pointer?
No. This line of your program is incorrect:
int **ptr = (int**)matrix;
This answer deals with the same topic
If you want concrete image how multidimensional arrays are implemented:
The rules for multidimensional arrays are not different from those for ordinary arrays, just substitute the "inner" array type as element type. The array items are stored in memory directly after each other:
matrix: 11 22 33 99 44 55 66 110
----------- the first element of matrix
------------ the second element of matrix
Therefore, to address element matrix[x][y], you take the base address of matrix + x*4 + y (4 is the inner array size).
When arrays are passed to functions, they decay to pointers to their first element. As you noticed, this would be int (*)[4]. The 4 in the type would then tell the compiler the size of the inner type, which is why it works. When doing pointer arithmetic on a similar pointer, the compiler adds multiples of the element size, so for matrix_ptr[x][y], you get matrix_ptr + x*4 + y, which is exactly the same as above.
The cast ptr=(int**)matrix is therefore incorrect. For once, *ptr would mean a pointer value stored at address of matrix, but there isn't any. Secondly, There isn't a pointer to matrix[1] anywhere in the memory of the program.
Note: the calculations in this post assume sizeof(int)==1, to avoid unnecessary complexity.
No. A multidimensional array is a single block of memory. The size of the block is the product of the dimensions multiplied by the size of the type of the elements, and indexing in each pair of brackets offsets into the array by the product of the dimensions for the remaining dimensions. So..
int arr[5][3][2];
is an array that holds 30 ints. arr[0][0][0] gives the first, arr[1][0][0] gives the seventh (offsets by 3 * 2). arr[0][1][0] gives the third (offsets by 2).
The pointers the array decays to will depend on the level; arr decays to a pointer to a 3x2 int array, arr[0] decays to a pointer to a 2 element int array, and arr[0][0] decays to a pointer to int.
However, you can also have an array of pointers, and treat it as a multidimensional array -- but it requires some extra setup, because you have to set each pointer to its array. Additionally, you lose the information about the sizes of the arrays within the array (sizeof would give the size of the pointer). On the other hand, you gain the ability to have differently sized sub-arrays and to change where the pointers point, which is useful if they need to be resized or rearranged. An array of pointers like this can be indexed like a multidimensional array, even though it's allocated and arranged differently and sizeof won't always behave the same way with it. A statically allocated example of this setup would be:
int *arr[3];
int aa[2] = { 10, 11 },
ab[2] = { 12, 13 },
ac[2] = { 14, 15 };
arr[0] = aa;
arr[1] = ab;
arr[2] = ac;
After the above, arr[1][0] is 12. But instead of giving the int found at 1 * 2 * sizeof(int) bytes past the start address of the array arr, it gives the int found at 0 * sizeof(int) bytes past the address pointed to by arr[1]. Also, sizeof(arr[0]) is equivalent to sizeof(int *) instead of sizeof(int) * 2.
In C, there's nothing special you need to know to understand multi-dimensional arrays. They work exactly the same way as if they were never specifically mentioned. All you need to know is that you can create an array of any type, including an array.
So when you see:
int matrix[2][4];
Just think, "matrix is an array of 2 things -- those things are arrays of 4 integers". All the normal rules for arrays apply. For example, matrix can easily decay into a pointer to its first member, just like any other array, which in this case is an array of four integers. (Which can, of course, itself decay.)
If you can use the stack for that data (small volume) then you usually define the matrix:
int matrix[X][Y]
When you want to allocate it in the heap (large volume), the you usually define a:
int** matrix = NULL;
and then allocate the two dimensions with malloc/calloc.
You can treat the 2d array as int** but that is not a good practice since it makes the code less readable. Other then that
**matrix == matrix[0][0] is true

Pointers equivalent to Arrays

I know that If i define an array like
int a [10];
I can use a pointer notation, to access it's address using a+<corresponding_item_in_array>
and it's value using, *(a+<corresponding_item_in_array>) .
Now I wanted to reverse things, I used malloc to allocate a memory to a integer pointer, and tried to represent the pointer in subscript notation but it didn't work
int *output_array;
output_array = (int *) (malloc(2*2*2*sizeof(int))); //i.e, space for 3d array
output_array[0][0][1] = 25;
// ^ produces error: subscripted value is neither array nor pointer
I may have used an pointer expression using Storage Mapping, but ain't the simpler method available? and Why?
The int* type is not an equivalent of the 3D array type; it is an equivalent of a 1D array type:
int *output_array;
output_array = (int *) (malloc(8*sizeof(int))); //i.e, space for array of 8 ints
output_array[5] = 25; // This will work
The problem with arrays of higher ranks is that in order to index into a 2D, 3D, etc. array the compiler must know the size of each dimension except the first one in order to calculate the offsets from the indexes correctly. To deal with 3D arrays, define a 2D element, like this:
typedef int element2d[2][2];
Now you can do this:
element2d *output_array;
output_array = (element2d*) (malloc(2*sizeof(element2d)));
output_array[0][0][1] = 25; // This will work now
Demo on ideone.
What's the type of output_array? int *.
What's the type of *(output_array+n) or output[n]? int.
Is subscript permitted on int? Both subscripts (eg. *(output_array+n) and output[n]) are pointer operations, and int is not a pointer. This explains the error you recieved.
You can declare a pointer to int[x][y] like this: int (*array)[x][y];
You can allocate storage that's a suitable alternative to a 3D array to array using: array = malloc(42 * x * y);. This would be the equivalent to int array[42][x][y];, except that arrays aren't modifiable lvalues, the alignof, sizeof and address-of operators work differently and the storage duration is different.
Because compiler doesn't have any idea about the size of each dimension, so it's not able to find out where output_array[0][0][1] should be.
You could try this
typedef int (* array3d)[2][2];
array3d output_array;
output_array = (array3d) malloc(2 * 2 * 2 * sizeof(int));
output_array[0][0][1] = 25;
printf("%d\n", output_array[0][0][1]);

How does C allocate data items in a multidimensional array?

I'd like to find out how C will allocate a the data items of a multidimensional array, and if their allocation is consistent across machines.
I know that, at the lowest level, the data items are neighbours, but I don't know how they're arranged further up.
For example, if I allocate a 3D array as int threeD[10][5][6], can I assume that &(threeD[4][2][5]) + 1 == &(threeD[4][3][0])? On all machines?
Thanks in advance for your help.
Yes, arrays are stored in row major order across all implementations of C compilers.
The Standard says (I applied some reformatting):
6.5.2.1 Array subscripting
Constraints
3 Successive subscript operators designate an element of a multidimensional
array object.
If E is an n-dimensional array (n >= 2) with dimensions i * j * . . . * k,
then E (used a s other than an lvalue) is converted to a pointer to an
(n - 1)-dimensional array with dimensions j * . . . * k.
If the unary * operator is applied to this pointer explicitly, or
implicitly as a result of subscripting, the result is the pointed-to
(n - 1)-dimensional array, which itself is converted into a pointer if
used as other than an lvalue. It follows from this that arrays are stored
in row-major order (last subscript varies fastest).
The C standard is very specific in equating array subscripting with pointer arithmetic, and specifies that arrays are stored in row major order.
Consider the array object defined by the declaration
int x[3][5];
Here x is a 3 x 5 array of ints; more precisely, x is an array of three element objects, each of which is an array of five ints. In the expression x[i], which is equivalent to
(*((x)+(i))), x is first converted to a pointer to the initial array of five ints. Then
i is adjusted according to the type of x, which conceptually entails multiplying i by the size of the object to which the pointer points, namely an array of five int objects. The results are added and indirection is applied to yield an array of five ints. When used in the expression x[i][j], that array is in turn converted to a pointer to the first of the ints, so x[i][j] yields an int.
The elements are stored in Row Major order. So Elements along the last dimension are contiguous. However, elements between rows (as indicated by your example) aren't guaranteed to be contiguous. It depends on how the initial memory has been allocated.
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
// only elements in a single row are guaranteed to be
// contiguous because of the multiple mallocs
void main(void)
{
// 3 rows, 4 columns
int *a[3];
for ( int row = 0; row < 3; row++ )
a[row] = (int *)malloc(4*sizeof(int));
}
// all elements are guaranteed to be contiguous
// in a row major order.
void main(void)
{
// 3 rows, 4 columns
int *a[3];
int *buf = (int *)malloc(3*4*sizeof(int));
for ( int row = 0; row < 3; row++ )
a[row] = buf+4*row;
assert( (&a[1][3] + 1) == &a[2][0] );
}
Firstly, In C language address arithmetic is only defined within the boundaries of a given array. (I wanted to say "single-dimensional (SD) array", but technically all arrays in C are SD. Multi-dimensional arrays are built as SD arrays of SD arrays. And this view of arrays is the most appropriate for this topic). In C you can start from the pointer to the beginning of an array and move back and forth within that array using additive operations. You are not allowed to cross the boundaries of the array you started from, except that it is legal to form a pointer to an imaginary element that follows the last element. However, when it comes to accessing elements (reading and writing), you are only allowed to access the real, existing elements of the array you started from.
Secondly, in your example '&threeD[4][2][5] + 1' you are forming a pointer to the imaginary "past-the-last" element of array 'threeD[4][2]'. This by itself is legal. However, the language specification does not guarantee that this pointer is equal to the address of '&threeD[4][3][0]'. The only thing that it says is that it might be equal to it. It is true, that the other requirements imposed on arrays by the language specification pretty much "force" this relationship to hold. But it is not formally guaranteed. Some pedantic (to the point of being malicious) implementation is perfectly allowed to use some kind of compiler magic to break this relationship.
Thirdly, actually accessing '*(threeD[4][2][5] + 1)' is always illegal. Even if the pointer is pointing into the next array, the compiler is allowed to perform the necessary run-time checks and generate a segmentation fault, since you are using pointer arithmetic on 'threeD[4][2]' array and trying to access something outside its boundaries.
Fourthly, doing 'threeD[4][2][5] + 2', '...+ 3' etc. is always illegal for similar reasons (remember: one past the end is OK, but 2, 3 or more is illegal).
And finally, fifthly: yes I know that in many (if not most) (if not all) practical cases interpreting a 'T A[2][3][4]' array as a flat 'T A[2*3*4]' array will work. But, again, from the formal language point of view this is illegal. And don't be surprised if this perfectly working code will one day trigger a huge amount of warnings from some static or dynamic code analysis tool, if not from the compiler itself.

Resources