The * operator is what we call the dereference operator. To understand what it does, you must understand exactly what a pointer is.
When you do
char *p;
the "variable" p does not use the same amount of memory as a normal char, it uses more memory: it uses the amount of memory needed to correctly identify a memory position in your computer. So, let's say you use a 32-bit architecture, the variable p occupies 4 bytes (not the 1 byte you would expect from a char).
So, when you do
p = a;
you see clearly that you are changing the contents of the variable p, that is, you are putting another 32-bit number inside it: you are changing the address it is pointing to.
After that line executes, the value of p is the memory address of the character array a.
Now for the dereference operator. When you do
*p = 'Z';
you are telling the compiler that you want to store the value 'Z' ON THE ADDRESS pointed by p. So, the value of p remains the same after this line: it continues to point to the same address. It's the value of this address that has changed, and now contains 'Z'.
So, the final effect of
char a[] = {'a', 'b', 'c'};
char p = a;
*p = 'Z';
is the same as changing the first position of the array a to 'Z', that is:
char a[] = {'a', 'b', 'c'};
a[0] = 'Z';
NOTE: there is a difference when making a pointer point to an array: the variable that contains the array contains only the address of the first element, so a is the same as "the starting address of the array".
Usually you will see the & operator. It is an operator used to obtain the memory address of a variable. For example:
int number = 42;
int pointer = &number;
printf("%d", *pointer);
Here we have them all. The first line creates an integer variable and stores 42 inside it.
The second line creates a pointer to an integer, and stores the address of the variable number inside it.
The third line reades the value on the address pointed by the pointer.
So, the trick is to read *x as on the address pointed by x and &x as the address of x.