int data[3][5];
is a 3-element array of 5-element arrays.
Why? Intuitively for me if int[3]
is a 3-element array and int[3][5]
Should be a 5-element array of 3-elements arrays.
int data[3][5];
is a 3-element array of 5-element arrays.
Why? Intuitively for me if int[3]
is a 3-element array and int[3][5]
Should be a 5-element array of 3-elements arrays.
The intuition should come from the indexing convention - since it is an array of arrays, first index is selecting the element which is an array, the second index is selecting the element of the selected array. That is:
data[2][4]
will select element number 4 of the array number 2 (mind the zero-basing).
Now the definition of such an array seems to be a bit counter-intuitive as you noted, but apparently it is this way just to be consistent with indexing syntax, otherwise it will be much more confusing.
C doesn't always work in an intuitive way because of things like the spiral rule, though maybe you're mis-applying it here.
As with any language, you need to accept the syntax for what it is, not what you think it is, or you'll constantly be fighting with the language on a semantic level.
Tools like cdecl explain it as:
declare data as array 3 of array 5 of int
This falls out of C's concept of declarators. The pointer-ness, array-ness, or function-ness of a declaration is specified in the declarator, while the type-ness is specified with a type specifier1:
int *p; // *p is the declarator
double arr[N][M]; // arr[N][M] is the declarator
char *foo( int x ); // *foo( int x ) is the declarator
This allows you to create arbitrarily complex types in a compact manner:
int *(*foo(void))[M][N];
foo
is a function taking no parameters, returning a pointer to an M-element array of N-element arrays of pointer to int
.
Thus, the actual type of an object or function is specified through the combination of the type specifier (and any qualifiers) and the declarator.
Unfortunately, "compact" is just another way of saying "eye-stabby". Declarations like that can be hard to read and understand. It does mean that things like multi-dimensional array declarations read kind of "backwards":
+---------------------------------+
| |
v |
type arr -> array-of -> array-of -+
^
|
start here
But, if you work it through, it does make sense. Let's start with some arbitrary type T
. We declare an array of T
as
T arr[N];
Thus, arr
is an N-element array of T
. Now we replace T
with an array type R [M]
. This gives us
R arr[N][M];
arr
is still an N-element array of something, and that something is R [M]
, which is why we write arr[N][M]
instead of arr[M][N]
.