In C declarators, the *
operator associates right-to-left, similarly to the unary operators, and the array declarator associates left-to-right with a higher precedence. That is to say, char ***a[1][1]
, if we add full parentheses, is the same thing as: char (*(*(*((a[1])[1]))))
. The postfix []
type construction operators apply first, left to right, and then the unary *
operators right to left. This means that a
is an "array of arrays of pointers to pointer to pointer to char".
Parentheses can be used to override the association and precedence. For example: char (***a)[1][1]
. Now a is a "pointer to pointer to pointer to array of arrays of char". The *
operators apply right to left, and then the postfix ones to that parenthesized unit.
Other parenthesizations are possible, like char **(*a)[1][1]
, where a is now a pointer to an array, or char **(*a[1])[1]
, where a is an array of pointers.
The syntax determines the "shape" of the type.
A cast expression resembles a declaration, except that the declared name is simply removed. For instance to turn char **(*a[1])[1]
into a type expression suitable for a cast, we take out the a
: char **(*[1])[1]
.
The declarator syntax is designed in such a way that when we take out the declared identifier from the declarator, it is still obvious where that name would go if we were to put it back. It can only go in one place.
Given char **(*[1])[1]
from which a
has been removed, where can we re-insert a
?
char *a*(*[1])[1] // not here
char **a(*[1])[1] // not here
char **(a*[1])[1] // not here
char **(*a[1])[1] // aha! this rings a bell
char **(*[a 1])[1] // certainly not here
char **(*[1 a])[1] // nope
char **(*[1]a)[1] // wrong
char **(*[1])a[1] // wrong
char **(*[1])[a 1] // wrong
char **(*[1])[1 a] // wrong
char **(*[1])[1]a // wrong
Once you get used to the syntax, it will be obvious. The identifier has to either immediately follow the declaration specifiers (in this case char
) or has to immediately follow an open parenthesis (
or a *
operator. On the right, it can only be followed by [
or nothing.
So the first substitution immediately looks wrong, since a
is followed by *
, and the second substitution looks wrong because a
is followed by (
.