The purpose of a pointer is to save the address of a specific variable. Then the memory structure of following code should look like:
int a = 5;
int *b = &a;
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:
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.