I was just wondering why it is allowed to omit the leftmost index of a multidimensional array when passing the array to a function ? Why not more than one indexes? And how d
In a declaration
Actually, you can't leave out the rightmost or the leftmost dimension entirely.
However, the leftmost only can be deduced for you if you have an initialiser.
In a function argument list
When you pass an array into a function by value, you're actually passing a pointer to the first element of that array. Yes, it looks from the syntax like you're passing an array but, no, you're not.
Consider:
void f(int ar[3])
void f(int ar[])
Both are confusing syntax for the equivalent:
void f(int* ar)
No trace of an array, let alone one of specifically three elements.
Now:
void f(int ar[][3])
This is confusing syntax for the equivalent:
void f(int (*ar)[3])
where int (*)[3]
is the type of a pointer to the first element of your array (pointer to int[3]
).
In conclusion, don't pay too much attention to the array-like syntax that looks like []
; it's not really representative of what's truly happening.
Other answers described how the C standard handles array to pointer conversion and how this affects the function declaration, but I feel they didn't go into the why, so here I go...
In C, arrays stand for tightly packed elements in memory.
A -> _ _ _ _ _ _ ...
i: 0 1 2 3 4 5 ...
In the above example, each of the array elements is 1 _
wide. To find the i-th element, we thus have to go to the i-th address. (Note that the leftmost dimension (the size) doesn't matter here)
Now consider a multidimensional array:
B -> [_ _ _][_ _ _][_ _ _][_ _ _]...
i: 0 0 0 1 1 1 2 2 2 3 3 3
j: 0 1 2 0 1 2 0 1 2 0 1 2
^first row ^third row
To find the offset of A[i][j]
we need to jump over i rows (3*i) and then over j elements -> (3*i + j). Note how the size of the first dimension is also not needed here.
It should be clear by now then, that the leftmost size isn't needed when using the array, it is only needed when you create it.
Since there is no need to give the dimension of the leftmost index, then why not give it anyway, for completeness sake? After all, this is what is done in the Pascal programming language (a C contemporary).
Well, most functions that operate on arrays work the same for all possible array lengths, so specifying the size would only hurt your ability to reuse them.
for example, why do
int sum(int arr[10]){
int s = 0, i;
for(i=0; i<10; i++){
s += arr[i];
}
return s;
}
When you can do this instead:
int sum(int arr[], int n){
int s = 0, i;
for(i=0; i<n; i++){
s += arr[i];
}
return s;
}
As for omitting more then one dimension, this isn't possible when using normal multidimensional arrays (because you need to know the dimension to know when the first row ends and the second starts). However, if you are willing to spend some (little) extra memory for scratch space, it is perfectly possible to use pointers to pointers instead: http://www.eskimo.com/~scs/cclass/int/sx9b.html
Except when it is the operand of the sizeof
or unary &
operators, or is a string literal being used to initialize an array in a declaration, an expression of type "N-element array of T
" will have its type implicitly converted to "pointer to T
and will evaluate to the address of the first element in the array.
What does any of that have to do with your question?
Assume the following lines of code:
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
foo(arr);
We pass the array expression arr
as an argument to foo
. Since arr
is not an operand of either sizeof
or &
, its type is implicitly converted from "10-element array of int
" to "pointer to int
". Thus, we are passing a pointer value to foo
, not an array.
It turns out that in a function parameter declaration, T a[]
and T a[N]
are synonyms for T *a
; all three declare a
as a pointer to T
, not an array of T
.
We can write the prototype definition for foo
as
void foo(int *a) // <- foo receives a pointer to int, not an array
or
void foo(int a[]) // <-- a[] is a synonym for *a
Both mean the same thing; both declare a
as a pointer to int
.
Now let's look at multidimensional arrays. Assume the following code:
int arr[10][20];
foo(arr);
The expression arr
has type "10-element array of 20-element array of int
". By the rule described above, it will implicitly be converted to "pointer to 20-element array of int
". Thus, the prototype definition for foo
can be written as
void foo(int (*a)[20]) // <-- foo receives a pointer to an array, not an array of arrays
or
void foo(int a[][20]) // <-- a[][20] is a synonym for (*a)[20]
Again, both declare a
as a pointer, not an array.
This is why you can drop the leftmost (and only the leftmost) array index in a function parameter declaration.