Pointers to pointers vs. normal pointers

Solution 1:

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.

Solution 2:

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

At runtime, yes, a pointer just holds an address. But at compile time there is also a type associated with every variable. As the others have said, int* and int** are two different, incompatible types.

There is one type, void*, that does what you want: It stores only an address, you can assign any address to it:

int a = 5;
int *b = &a;
void *c = &b;

But when you want to dereference a void*, you need to supply the 'missing' type information yourself:

int a2 = **((int**)c);

Solution 3:

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.

  • variables have types
  • variables hold values
  • a pointer is a value
  • a pointer refers to a variable
  • if p is a pointer value then *p is a variable
  • if v is a variable then &v is a pointer

And 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.

Solution 4:

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.

Solution 5:

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.