why c/c++ allows omission of leftmost index of a multidimensional array in a function call? why c/c++ allows omission of leftmost index of a multidimensional array in a function call? c c

why c/c++ allows omission of leftmost index of a multidimensional array in a function call?


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 3j:    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


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.


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.