A variable of a pointer type, like any other variable, has a memory address and may store a value. The thing is that the type of values that a pointer variable may store is also a memory address.
You can access the address of a variable by preceding its name with the address operator
& while you can access the value of a variable using its name. Given the following 2 variables
char c = 'a';
char *s0 = "foo";
printf("%c\n", c); // prints 'a'
printf("%p\n", &c); // prints the memory address of c
printf("%s\n", s0); // prints "foo"
printf("%p\n", &s0); // prints the memory address of s
This might be a little bit confusing but char pointers are somehow special in C in the sense that you may do something like
char *s0 = "foo";
in which case the string "foo" will be stored somewhere in a data segment in memory. The address of this location is stored inside the pointer variable
s0. Yet we could directly assign the string "foo" to
s0, a thing which wouldn't work with other pointer types. For example
// int *p = 10; /* would not store 10 in the locate pointed to by p */
So what is actually stored in
s0 is NOT "foo", but the memory address where "foo" is stored.
When you do something like
char *s1 = s0;
The memory address that is stored in
s0 gets also stored in
s1. Recall that this memory address is the value that
s1 store so if you do something like
printf("%p\n", s0); // prints the value stored in s0 (let's say 0x123)
printf("%p\n", s1); /* prints the value stored in s1 which is the SAME
value stored in s0 (0x123 in this case)*/
s1 are two separate variables that store the same value. Because they are two separate variables, each one of them represents a different location in memory and thus each one of them has its own memory address. Recall that you can access the address of a variable using the address operator (i.e.,
&). So doing something like
printf("%p\n", &s0); // prints the address of s0
printf("%p\n", &s1); /* prints the address of s1 (which is DIFFERENT
from the address of s0 */