Here's the grammar for a declarator as taken from the standard (§ 6.7.5):
declarator:
pointeropt direct-declarator
direct-declarator:
identifier
(
declarator )
direct-declarator [
type-qualifier-listopt assignment-expressionopt ]
direct-declarator [ static
type-qualifier-listopt assignment-expression ]
direct-declarator [
type-qualifier-list static
assignment-expression ]
direct-declarator [
type-qualifier-listopt * ]
direct-declarator (
parameter-type-list )
direct-declarator (
identifier-listopt )
pointer:
*
type-qualifier-listopt
*
type-qualifier-listopt pointer
type-qualifier-list:
type-qualifier
type-qualifier-list type-qualifier
parameter-type-list:
parameter-list
parameter-list , ...
parameter-list:
parameter-declaration
parameter-list ,
parameter-declaration
parameter-declaration:
declaration-specifiers declarator
declaration-specifiers abstract-declaratoropt
identifier-list:
identifier
identifier-list ,
identifier
As you can see, both []
and ()
bind to the declarator before *
. Take the declaration
int *a[N];
The declarator is *a[N]
, which fits the pointeropt direct-declarator pattern above, and is thus parsed as *(a[N])
, so a
is an N-element array of pointer.
To sum up:
T *a[N] -- declares an N-element array of pointer to T
T (*a)[N] -- declares a pointer to an N-element array of T
T *f() -- declares a function returning pointer to T
T (*f)() -- declares a pointer to a function returning T