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;
In
int a = 5;
int *b = &a;
int *c = &b;
You get a warning because &b
is of type int **
, and you try to initialize a variable of type int *
. There's no implicit conversions between those two types, leading to the warning.
To take the longer example you want to work, if we try to dereference f
the compiler will give us an int
, not a pointer that we can further dereference.
Also note that on many systems int
and int*
are not the same size (e.g. a pointer may be 64 bits long and an int
32 bits long). If you dereference f
and get an int
, you lose half the value, and then you can't even cast it to a valid pointer.
The type system of C requires this, if you want to get a correct warning and if you want the code to compile at all. With only one level of depth of pointers you wouldn't know if the pointer is pointing to a pointer or to an actual integer.
If you dereference a type int**
you know the type you get is int*
and similarly if you dereference int*
the type is int
. With your proposal the type would be ambiguous.
Taking from your example, it is impossible to know whether c
points to a int
or int*
:
c = rand() % 2 == 0 ? &a : &b;
What type is c pointing to? The compiler doesn't know that, so this next line is impossible to perform:
*c;
In C all type information is lost after compiling, as every type is checked at compile-time and isn't needed anymore. Your proposal would actually waste memory and time as every pointer would have to have additional runtime information about the types contained in pointers.
There are different types. And there is a good reason for it:
Having …
int a = 5;
int *b = &a;
int **c = &b;
… the expression …
*b * 5
… is valid, while the expression …
*c * 5
makes no sense.
The big deal is not, how pointers or pointers-to-pointers are stored, but to what they refer.
Pointers are abstractions of memory addresses with additional type semantics, and in a language like C type matters.
First of all, there's no guarantee that int *
and int **
have the same size or representation (on modern desktop architectures they do, but you can't rely on it being universally true).
Secondly, the type matters for pointer arithmetic. Given a pointer p
of type T *
, the expression p + 1
yields the address of the next object of type T
. So, assume the following declarations:
char *cp = 0x1000;
short *sp = 0x1000; // assume 16-bit short
int *ip = 0x1000; // assume 32-bit int
long *lp = 0x1000; // assume 64-bit long
The expression cp + 1
gives us the address of the next char
object, which would be 0x1001
. The expression sp + 1
gives us the address of the next short
object, which would be 0x1002
. ip + 1
gives us 0x1004
, and lp + 1
gives us 0x1008
.
So, given
int a = 5;
int *b = &a;
int **c = &b;
b + 1
gives us the address of the next int
, and c + 1
gives us the address of the next pointer to int
.
Pointer-to-pointers are required if you want a function to write to a parameter of pointer type. Take the following code:
void foo( T *p )
{
*p = new_value(); // write new value to whatever p points to
}
void bar( void )
{
T val;
foo( &val ); // update contents of val
}
This is true for any type T
. If we replace T
with a pointer type P *
, the code becomes
void foo( P **p )
{
*p = new_value(); // write new value to whatever p points to
}
void bar( void )
{
P *val;
foo( &val ); // update contents of val
}
The semantics are exactly the same, it's just the types that are different; the formal parameter p
is always one more level of indirection than the variable val
.
If the purpose of pointer is just to save the memory address, I think there should be no hierarchy if the address we are going to save refers variable, pointer, double pointer, ... etc. so below type of code should be valid.
I think here is your misunderstanding: The purpose of the pointer itself is to store the memory address, but a pointer usually as well has a type so that we know what to expect at the place it points to.
Especially, unlike you, other people really want to have this kind of hierarchy so as to know what to do with the memory contents which is pointed to by the pointer.
It is the very point of C's pointer system to have type information attached to it.
If you do
int a = 5;
&a
implies that what you get is a int *
so that if you dereference it is an int
again.
Bringing that to the next levels,
int *b = &a;
int **c = &b;
&b
is a pointer as well. But without knowing what hides behind it, resp. what it points to, it is useless. It is important to know that dereferencing a pointer reveals the type of the original type, so that *(&b)
is an int *
, and **(&b)
is the original int
value we work with.
If you feel that in your circumstances there should be no hierarchy of types, you can always work with void *
, although the direct usability is quite limited.
Now my question is, why does this type of code,
int a = 5; int *b = &a; int *c = &b;
generate a warning?
You need to go back to the fundamentals.
p
is a pointer value then *p
is a variablev
is a variable then &v
is a pointerAnd now we can find all the mistakes in your posting.
Then assume that now I want to save the address of pointer
*b
No. *b
is a variable of type int. It is not a pointer. b
is a variable whose value is a pointer. *b
is a variable whose value is an integer.
**c
refers to the address of*b
.
NO NO NO. Absolutely not. You have to understand this correctly if you are going to understand pointers.
*b
is a variable; it is an alias for the variable a
. The address of variable a
is the value of variable b
. **c
does not refer to the address of a
. Rather, it is a variable that is an alias for variable a
. (And so is *b
.)
The correct statement is: the value of variable c
is the address of b
. Or, equivalently: the value of c
is a pointer that refers to b
.
How do we know this? Go back to the fundamentals. You said that c = &b
. So what is the value of c
? A pointer. To what? b
.
Make sure you fully understand the fundamental rules.
Now that you hopefully understand the correct relationship between variables and pointers, you should be able to answer your question about why your code gives an error.