C doesn't have any specific support for multidimensional arrays. A two-dimensional array such as double inputMatrix[N][M]
is just an array of length N
whose elements are arrays of length M
of doubles.
There are circumstances where you can leave off the number of elements in an array type. This results in an incomplete type — a type whose storage requirements are not known. So you can declare double vector[]
, which is an array of unspecified size of doubles. However, you can't put objects of incomplete types in an array, because the compiler needs to know the element size when you access elements.
For example, you can write double inputMatrix[][M]
, which declares an array of unspecified length whose elements are arrays of length M
of doubles. The compiler then knows that the address of inputMatrix[i]
is i*sizeof(double[M])
bytes beyond the address of inputMatrix[0]
(and therefore the address of inputMatrix[i][j]
is i*sizeof(double[M])+j*sizeof(double)
bytes). Note that it needs to know the value of M
; this is why you can't leave off M
in the declaration of inputMatrix
.
A theoretical consequence of how arrays are laid out is that inputMatrix[i][j]
denotes the same address as inputMatrix + M * i + j
.¹
A practical consequence of this layout is that for efficient code, you should arrange your arrays so that the dimension that varies most often comes last. For example, if you have a pair of nested loops, you will make better use of the cache with for (i=0; i<N; i++) for (j=0; j<M; j++) ...
than with loops nested the other way round. If you need to switch between row access and column access mid-program, it can be beneficial to transpose the matrix (which is better done block by block rather than in columns or in lines).
C89 references: §3.5.4.2 (array types), §3.3.2.1 (array subscript expressions)
C99 references: §6.7.5.2 (array types), §6.5.2.1-3 (array subscript expressions).
¹ Proving that this expression is well-defined is left as an exercise for the reader. Whether inputMatrix[0][M]
is a valid way of accessing inputMatrix[1][0]
is not so clear, though it would be extremely hard for an implementation to make a difference.