Only the *
, []
, and ()
operators have any meaning in declarations (C++ adds &
, but we won't go into that here).
In the declaration
int *p;
the int
-ness of p
is specified by the type specifier int
, while the pointer-ness of p
is specified by the declarator *p
.
The type of p
is "pointer to int
"; this type is fully specified by the combination of the type specifier int
plus the declarator *p
.
In a declaration, the declarator introduces the name of the thing being declared (p
) along with additional type information not given by the type specifier ("pointer to"):
T v; // v is a single object of type T, for any type T
T *p; // p is a pointer to T, for any type T
T a[N]; // a is an N-element array of T, for any type T
T f(); // f is a function returning T, for any type T
This is important - pointer-ness, array-ness, and function-ness are specified as part of the declarator, not the type specifier1. If you write
int* a, b, c;
it will be parsed as
int (*a), b, c;
so only a
will be declared as a pointer to int
; b
and c
are declared as regular int
s.
The *
, []
, and ()
operators can be combined to create arbitrarily complex types:
T *a[N]; // a is an N-element array of pointers to T
T (*a)[N]; // a is a pointer to an N-element array of T
T *(*f[N])(); // f is an N-element array of pointers to functions
// returning pointer to T
T *(*(*(*f)[N])())[M] // f is a pointer to an N-element array of pointers
// to functions returning pointers to M-element
// arrays of pointers to T
Notice that *
, []
, and ()
obey the same precedence rules in declarations that they do in expressions. *a[N]
is parsed as *(a[N])
in both declarations and expressions.
The really important thing to realize in this is that the form of a declaration matches the form of the expression in code. Going back to our original example, we have a pointer to an integer named p
. If we want to retrieve that integer value, we use the *
operator to dereference p
, like so:
x = *p;
The type of the expression *p
is int
, which follows from the declaration
int *p;
Similarly, if we have an array of pointers to double
and we want to retrieve a specific value, we index into the array and dereference the result:
y = *ap[i];
Again, the type of the expression *ap[i]
is double
, which follows from the declaration
double *ap[N];
So why doesn't ++
play a role in a declaration like *
, []
, or ()
? Or any other operator like +
or ->
or &&
?
Well, basically, because the language definition says so. It only sets aside *
, []
, and ()
to play any role in a declaration, since you have to be able to specify pointer, array, and function types. There's no separate "increment-this" type, so there's no need for ++
to be part of a declaration. There's no "bitwise-this" type, so no need for unary &
, |
, ^
, or ~
to be part of a declaration either. For types that use the .
member selection operator, we use the struct
and union
tags in the declaration. For types that use the ->
operator, we use the struct
and union
tags in conjunction with the *
operator in the declarator.
- Of course, you can create typedef names for pointer, array, and function types, like
typedef int *iptr;
iptr a,b,c; // all three of a, b, and c are pointers to int
but again, it's the declarator *iptr
that specifies the pointer-ness of the typedef name.
void move(int *units) { ... }
, it is an indirection operator. Considered part of the type, it can also be writtenvoid move(int* units) { ... }
, although I prefer the former style. You read both as "int pointer." See also stackoverflow.com/a/8911253