From what I remember arrays are always passed as pointers. For instance, the declaration:
void foo(int array[2][5]);
means for compiler exactly the same thing as:
void foo(int (*array)[5]);
You can say that these both forms are equivalent. Now, I wonder, why it is allowed then to declare it as:
void foo(int (*array)[]);
while not as:
void foo(int array[][]);
Take an example:
#include <stdio.h>
void foo(int (*p)[3]);
void bar(int (*p)[]);
int main(void)
{
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
foo(a);
bar(a);
return 0;
}
// The same as int p[][3] or int p[N][3] where N is a constant expression
void foo(int (*p)[3])
{
}
// Would it the same as int p[][] or int p[N][] (by analogy)?
void bar(int (*p)[])
{
}
It compiles fine and without warnings, but if I change bar's declaration to:
void bar(int p[][]);
then it's an error.
Why C allows such "obscure" way to pass an array?
Arrays are not pointers, if you declare an array of pointers with unspecified size it's ok because it will store the addresses stored in the poitners contigously and the size of each element is known, but p[][] would require the arrays to be contigous not their addresses and the size of the array is unknown so that's the problem.
To make it clear if you say int p[][] the you don't know how far is p[0] from p[1] whereas in int (*p)[] you know that the distance is the size of a pointer.
Arrays are converted to pointers but the are not pointers.
Arrays are usable as pointers, but they're more than pointers; they point to a number of things which have a size. That way, you can tell the compiler you're interested in the third or fifth element of your array, and it will calculate the location of that element by multiplying the size of one element by three or five to find an offset, and adding that offset to the address of the array.
C doesn't actually have multidimensional arrays. What it does have is arrays of arrays, which is pretty much the same thing. Say you have this:
int a[4][5];
In memory, this would look like this:
[0] |[1] |[2] |[3]
+-------------------|-------------------|-------------------|-------------------+
a | | | | | | | | | | | | | X | | | | | | | |
+-------------------|-------------------|-------------------|-------------------+
0 1 2 3 4 | 0 1 2 3 4 | 0 1 2 3 4 | 0 1 2 3 4 |
and you then try to access the element at index [2][2], then the system needs to perform the following calculation (conceptually):
In this case, the calculation is *(a + (2 * 5 * sizeof(int)) + (2 * sizeof(int))), giving *(a + 12*sizeof(int)), which is indeed the correct offset from the starting pointer.
All that means is that the size of the inner array needs to be defined in order for the compiler to be able to do that calculation. If you don't define the size of any dimension (but the leftmost one) of your multidimensional array, you don't have a defined size, and the compiler will balk.
You're tripping over the C language support for arrays of 'unspecified' size, which you can declare, but can't generally use directly unless you give it a more specific size somewhere.
In your example, if you actually try to do anything with the array in bar, you'll get an error:
void bar(int (*p)[])
{
printf("%d\n", p[1][1]);
}
% gcc -Wall t.c
t.c: In function ‘bar’:
t.c:23:5: error: invalid use of array with unspecified bounds
printf("%d\n", p[1][1]);
^
The only thing you can do with p in bar is cast it to some type with an explicit size, or pass it to some other function that takes a pointer to an array (of specified or unspecified size), and if you use the wrong explicit size to try access the array, you get undefined behavior with no warning.