Here's an example I've used before. Assume the following two functions:
void foo( T *p ) // where T is an arbitrary data type
{
*p = new_value(); // write new value to the thing p points to
}
void bar( void )
{
T var;
foo( &var ); // write new value to var
}
For function foo
to update the contents of var
(an object of type T
) we must pass a pointer to var
(type T *
).
If we replace T
with a pointer type P *
, then the above code becomes
void foo( P **p )
{
*p = new_value(); // write new value to the thing p points to
}
void bar( void )
{
P *var;
foo( &var ); // write new value to var
}
The semantics are exactly the same; we want foo
to write a new value to var
, so we pass a pointer to var
. It's just that in this case, var
is already a pointer type, so we wind up passing a pointer to a pointer.
Basically, for foo
to update the contents of var
, you must pass the expression &var
as the argument, meaning the type of the formal parameter p
will always have one more level of indirection than the type of var
.
Type of var Type of &var Type of p
----------- ------------ ---------
T T * T *
T * T ** T **
T ** T *** T ***
etc.