That would be because any pointer T*
is actually of type pointer to a T
(or address of a T
), where T
is the pointed-to type. In this case, *
can be read as pointer to a(n)
, and T
is the pointed-to type.
int x; // Holds an integer.
// Is type "int".
// Not a pointer; T is nonexistent.
int *px; // Holds the address of an integer.
// Is type "pointer to an int".
// T is: int
int **pxx; // Holds the address of a pointer to an integer.
// Is type "pointer to a pointer to an int".
// T is: int*
This is used for dereferencing purposes, where the dereference operator will take a T*
, and return a value whose type is T
. The return type can be seen as truncating the leftmost "pointer to a(n)", and being whatever's left over.
*x; // Invalid: x isn't a pointer.
// Even if a compiler allows it, this is a bad idea.
*px; // Valid: px is "pointer to int".
// Return type is: int
// Truncates leftmost "pointer to" part, and returns an "int".
*pxx; // Valid: pxx is "pointer to pointer to int".
// Return type is: int*
// Truncates leftmost "pointer to" part, and returns a "pointer to int".
Note how for each of the above operations, the dereference operator's return type matches the original T*
declaration's T
type.
This greatly aids both primitive compilers and programmers in parsing a pointer's type: For a compiler, the address-of operator adds a *
to the type, the dereference operator removes a *
from the type, and any mismatch is an error. For a programmer, the number of *
s is a direct indication of how many levels of indirection you're dealing with (int*
always points to int
, float**
always points to float*
which in turn always points to float
, etc.).
Now, taking this into consideration, there are two major issues with only using a single *
regardless of the number of levels of indirection:
- The pointer is much more difficult for the compiler to dereference, because it has to refer back to the most recent assignment to determine the level of indirection, and determine the return type appropriately.
- The pointer is more difficult for the programmer to understand, because it's easy to lose track of how many layers of indirection there are.
In both cases, the only way to determine the value's actual type would be to backtrack it, forcing you to look somewhere else to find it.
void f(int* pi);
int main() {
int x;
int *px = &x;
int *ppx = &px;
int *pppx = &ppx;
f(pppx);
}
// Ten million lines later...
void f(int* pi) {
int i = *pi; // Well, we're boned.
// To see what's wrong, see main().
}
This... is a very dangerous problem, and one that is easily solved by having the number of *
s directly represent the level of indirection.