Consider the following:
char* a;
char* const b = "Hello"
const char* c;
const char* const d = "world";
`a` just points to a character. That might be the first character of a string, or a single byte. That character can be modified by writing `*a='Z'` or `a[0]='q'` or by passing `a` to a function taking a `char*` parameter. The pointer itself can also be modified to point at some other characters, e.g. `a=b;` or `a="foo";`. This is the most flexible form, and the least safe, because you can do anything with it.
`b` is a constant pointer to a character. In this case, it's pointing at an array of characters. Those characters can be modified, e.g. `b[1]='a';` or `*b=0;` but `b` itself is constant, meaning that it cannot ever point at a different part of memory. This form may be used when you have allocated a buffer or array whose contents you want to modify, but which cannot move, since it was allocated by the operating system.
`c` points to a constant character. You can change what it points at (`c="foo";` or `c=b;`) but you cannot use this pointer to change the characters that it points at -- _even if it's pointing at the non-constant buffer_ `b`. This form is commonly used when handling pre-generated strings. You can use this variable to store strings you've found, and pass their address around, as long as you don't change the strings themselves.
`d` is a constant pointer to a constant character. You can't change the characters it's pointing at, and you can't point the pointer itself at a different memory location. All you can do is read the string or individual characters (`char e=d[4];` or `puts(d);`). This form is mainly used when passing around reference strings, for example when naming real objects.