In this declaration
char * message2 = "bye world";
there is declared a pointer that points to the first character of the string literal "bye world"
. You may reassign the pointer to point to another string as for example
message2 = "Bye";
The string literals themselves are not changed. It is the pointer that is changed. At first it pointed to the first character of one string literal and then it was reassigned by the address of the first character of another string literal.
This statement
char ** ptr_message2 = &message2;
only adds one more indirection. So the above statement
message2 = "Bye";
can be rewritten like
*ptr_message2 = "Bye";
Arrays opposite to pointers do not support the assignment operator. If you want to change a content of an array you need to change its elements separately. For example to copy a string to a character array you can write
#include <string.h>
//...
char message[] = "hello world";
strcpy( message, "Hello" );
As for your code then after this declaration
char ** ptr_message = (char*)&message;
the pointer ptr_message
stores the address of the extent of memory occupied by the array that is the same address as the address of the first character of the array.
Dereferencing the pointer
*ptr_message = "Hello";
the value stored in the first elements of the array is considered as a value of an address. As the stored string "hello world"
does represent a valid address then the dereferenced pointer has an invalid value and the statement above invokes undefined behavior.
Using a pointer to pointer you could write for example by introducing an intermediate pointer the following way
#include <string.h>
//...
char message[] = "hello world";
//...
char *ptr = message;
char ** ptr_message = &ptr;
//...
strcpy( *ptr_message, "Hello" );
Now dereferencing the pointer ptr_message
you get another pointer (instead of characters of a string literal) that points to the first character of the array and may use it to change elements of the array.
Pay attention to that in these declarations
char * message2 = "bye world";
and
char *ptr = message;
the arrays (string literals have types of character arrays) used as initializers are implicitly converted to pointers to their first elements.