First of all let’s remember what a pointer is. A pointer is an address pointing to an object. “Address” term is open for debate, some pedantic assess would argue against it.

So let’s start giving examples.

const char* str ="Foo";
str[0] = 'B'; // error: assignment of read-only location
str = "Bar"; // Perfectly OK

So, it is obvious that first line declares str as n pointer pointing to a constant object and the object is not modifiable but the pointer itself is.

Let’s review another example.

char * const str = "Foo";
str[0] = 'B'; // Perfectly OK (See the node below)
str = "Bar"; // error: assignment of read-only variable

Here we can see that the declaration tells compiler that the pointer value (address) is constant but the object it is pointing to can be modified. Please note that even though the above assignment “str[0] = ‘B’” will compile just fine, it will most probably result in a segmentation fault during runtime since your compiler will most likely put it in read only memory region.

Now let’s make another example.

const char * const str = "Foo";
str[0] = 'B'; // error: assignment of read-only location
str = "Bar"; // error: assignment of read-only variable

This declaration tells the compiler that neither the object the pointer is pointing to can be modified, nor the pointer value (address) itself. i.e. nothing is modifiable :)

Conclusion

So, we can conclude that qualifiers (const, volatile) that are placed before the * applies to the object the pointer is pointing to, and qualifiers placed after the * applies to the pointer value itself, i.e. the address.

So we can have weird looking declaration like this one:

extern volatile const char * volatile const str = "Foo";

I know it looks a bit unusual but it is perfectly valid.

Note: For hardware programming volatile keyword is a must. If you don’t know what it is, read thisĀ article at wiki.answers.com first. Here’s theĀ backup, just in case.