[Where I just re-learned this yesterday]
This is a C99 extension that is not widely known. The array is still passed by reference as they always have been, but the compiler is able to interpret it as an array similar to the way it handles Variable-Length Arrays.
This won't give you bound-checking, of course. C doesn't have that.
In your function signature,
int someFunction(int n, int arr[n][n]);
The final n
doesn't really buy you anything, it just gets ignored. But int arr[n][]
is new. That's what C89 didn't have. Previously, the only option was to calculate indices manually off of the base pointer, arr[n*x+y]
.
In Ansi-C (C89), you'd have to declare the function as,
int someFunction(int n, int arr[]);
which is equivalent to
int someFunction(int n, int *arr);
and calculate the two dimensions as a single index.
The new C99 magic, reduces the original to this:
int someFunction(int n, int *arr[n]);
I'd describe the process more as array adoption, than passing. It just mitigates some of the losses that were incurred by the original decision to pass arrays as pointers.
It's important to understand that second two examples are sort-of internalized equivalents. Figurative illustrations. In the final one, the array is not converted into an array of pointers, but the bounds of the lowest dimension is dropped. Perhaps it's better illustrated like this:
int someFunction(int n, int arr[n][]);
The only way to copy an array as a function argument is to wrap it in a struct. But then you cannot have variable dimensions.
struct arr {int n; int arr[n][n];}; //Nope, won't compile!
enum { n = 3 };
struct arr { int arr[n][n]; };
struct arr someFunction( struct arr ); //argument and return value are copied.
And this has been legal since 1989.