This is because in the first version you pass a pointer by value. This means that the actual pointer from main
copied, and in doit
you are editing only the copy.
In the second version, you emulate pass by reference (C has no corresponding links) by passing a pointer to a pointer.
Let's see if this becomes more clear to you.
For the first program in the main
function, you have a pointer c
, which points to the string literal "hello"
:
+ -------- + + --------- +
| main: c | ----> | "hello" |
+ -------- + + --------- +
Then, when you pass it to the function, the pointer is copied, so you have this:
+ -------- +
| main: c | -
+ -------- + \ + --------- +
> -> | "hello" |
+ -------- + / + --------- +
| doit: c | -
+ -------- +
After changing the pointer in doit
, you have the following:
+ -------- + + --------- +
| main: c | ----> | "hello" |
+ -------- + + --------- +
+ -------- + + --------- +
| doit: c | ----> | "world" |
+ -------- + + --------- +
For the second program, it starts with the same:
+ -------- + + --------- +
| main: c | ----> | "hello" |
+ -------- + + --------- +
But then it changes when you call with a pointer to a pointer:
+ -------- + + -------- + + --------- +
| doit: c | ----> | main: c | ----> | "hello" |
+ -------- + + -------- + + --------- +
Then dereferencing c
in doit
gives the original pointer c
from main
, and when you change it, you
+ -------- + + -------- + + --------- +
| doit: c | ----> | main: c | ----> | "world" |
+ -------- + + -------- + + --------- +