OK, in C declarations, you only get the first thing allocated that you read from a declaration.
This is a bit obscure until you understand how to read a declaration.
Operators have precedence, and that precedence applies in a declaration. For simplicity, take:
() - Either raises precedence, or means "function taking parameters and returning..."
[] - Array of
* - Pointer to (right associative)
So, the declaration:
char *a[3];
is an "array of 3 pointers to char", whereas
char (*a)[3];
is a "pointer to arrays of 3 char" (a pointer to rows of a multi-dim char array with row width 3)
The compiler allocates the very first thing on the stack. So, in the first example, you get three pointers that are capable of pointing to (an as yet unallocated) chars.
| ptr | --> (unallocated char)
| ptr | --> (unalloc char)
| ptr | --> (unalloc char)
in the second:
| ptr | --> (unallocated block of 3 char)
the next rule is: If you have a pointer, you have an array. So in both cases, you end up with multi-dimensional arrays.
In the first, the number of rows is fixed (you already allocated it), but each row can have a different size, as the compiler knows the pointer can be offset by a char to get to the next char.
In the second, you have a fixed row size, but the compiler knows how to offset your base pointer (by 3 chars each), so you have any number of rows.
C is also a call-by-value language. So, if you've declared a variable:
char *a[3];
then 'a' by itself is "an array of 3 ptr to char", but a[0] is "a ptr to a char", and a[0][0] is a char.
When you write a function that expects:
void f(char *arg[]) {
...
}
you must pass it an array of pointers to char. It does not care how many rows there are (you have to keep track of that)...it only cares that the type is correct.
So, if you have 'a' declared as in our first example, you have to call f(a). A function call does an assignment-like thing to the function args:
f(a) means call f with (arg = a).
The precedence rules are especially important, and you should work on these until you can read declarations like this:
int (*f(int (*a)[4], void (*f)()))[6];
which is "f is a function that takes:
- a pointer to arrays of 4 int
- a pointer to a function that takes nothing and returns nothing)
and (then f) returns a pointer to arrays of 6 int".