Reputation: 37
As we know, when passing arrays as function arguments, only the first dimension's size can be empty, the others must be specified.
void my_function(int arr[5][10][15]); // OKAY!
void my_function(int arr[][10][15]); // OKAY!
void my_function(int arr[][][15]); // WRONG!!!
void my_function(int arr[][][]); // WRONG!!!
What is logic behind this? Could someone explain the main reason?
Upvotes: 1
Views: 194
Reputation: 123448
Starting with two bits of standardese:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of thesizeof
operator, the_Alignof
operator, or the unary&
operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
...
6.7.6.3 Function declarators (including prototypes)
...
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the[
and]
of the array type derivation. If the keywordstatic
also appears within the[
and]
of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.
Lovely. What does all that mean?
Let's start with the declaration of an array of some arbitrary type T
:
T arr[N];
The type of the expression arr
is "N-element array of T
" (T [N]
). Unless that expression is the operand of the unary &
, sizeof
, or _Alignof
operators as specified in 6.3.2.1/3 above, then the type of the expression is converted ("decays") to "pointer to T
" (T *
) and the value of the expression is the address of the first element - i.e., &arr[0]
.
When we pass arr
as a parameter to a function, as in
foo( arr );
what foo
actually receives is a pointer, not an array, and you would write the function prototype as
void foo( T *arr )
Or...
As a notational convenience, C allows you to declare the formal parameter using array notation:
void foo( T arr[N] )
or
void foo( T arr[] )
In this case, both T arr[N]
and T arr[]
are "adjusted" to T *arr
as per 6.7.6.3/7 above, and all three forms declare arr
as a pointer (this is only true for function parameter declarations).
That's easy enough to see for 1D arrays. But what about multidimensional arrays?
Let's replace T
with an array type A [M]
. Our declaration now becomes
A arr[N][M]; // we're creating N M-element arrays.
The type of the expression arr
is "N-element array of M-element arrays of A
" (A [M][N]
). By the rule above, this "decays" to type pointer to *M-element array* of
A" (
A (*)[M]`). So when we call
foo( arr );
the corresponding prototype is
void foo( A (*arr)[M] )1
which can also be written as
void foo( A arr[N][M] )
or
void foo( A arr[][M] );
Since T arr[N]
and T arr[]
are adjusted to T *arr
, then A arr[N][M]
and A arr[][M]
are adjusted to A (*arr)[M]
.
Let's replace A
with another array type, R [O]
:
R arr[N][M][O];
The type of the expression arr
decays to R (*)[M][O]
, so the prototype of foo
can be written as
void foo( R (*arr)[M][O] )
or
void foo( R arr[N][M][O] )
or
void foo( R arr[][M][O] )
Are you starting to see the pattern yet?
When a multi-dimensional array expression "decays" to a pointer expression, only the first (leftmost) dimension is "lost", so in a function prototype declaration, only the leftmost dimension my be left blank.
[]
operator has a higher precedence than the unary *
operator, A *arr[M]
would be interpreted as "M-element array of pointers to A
", which is not what we want. We have to explicitly group the *
operator with the identifier to properly declare it as a pointer to an array.
Upvotes: 1
Reputation: 223739
Arrays in C and C++ are stored contiguously in memory. In the case of a multidimensional array, this means you have an array of arrays.
When you traverse an array, you don't necessarily need to know how many elements are in the array. You do however need to know the size of each element of the array so you can jump to the correct element.
That's why int arr[][][15]
is incorrect. It says that arr
is an array of unknown size elements are of type int [][15]
. But this means you don't know how big each array element is, so you don't know where each array element it in memory.
Upvotes: 5
Reputation: 138041
Passing arrays to a function is an illusion: arrays instantly decay to a pointer when you use them. That is, with this example:
int foo[10];
foo[1];
the way this is interpreted by the compiler, in foo[1]
, foo
is first transformed to a pointer to element 0 of foo
, and then subscripting is the same as *(pointer_to_foo + 1)
.
However, this is only possible with the first dimension of an array. Suppose this:
int foo[5][5];
This puts 25 integer elements contiguously in memory. It is not the same as int** foo
, and not convertible to int** foo
, because int** foo
represents some number of pointers to some number of pointers, and they don't have to be contiguous in memory.
While it's legal to use an array type as a function parameter, the compiler builds it identically to if you had specified a pointer to the array's element type:
int foo(int bar[10]); // identical to `int foo(int* bar)`
But what happens if you pass a multi-dimensional array?
int foo(int bar[5][5]); // identical to what?
Only the first dimension of the array can undergo decay. The equivalent signature for int foo
here would be:
int foo(int (*bar)[5]); // identical to `int foo(int bar[5][5])`
where int (*bar)[5]
is a pointer to an array of 5 integers. Stacking up more dimensions doesn't change the idea, only the first dimension will decay, and the other dimensions need to have a known size.
In other words, you can skip the first dimension because the compiler doesn't care about its size, as it instantly decays. However, subsequent dimensions do not decay at the call site, so you need to know their size.
Upvotes: 8