Let`s analyze this line.
volatile int * p; declares a pointer to a volatile int. A volatile int is from the storage and semantics a normal int. But volatile instructs the compiler, that its value could change anytime or has other side effects.
On the right side of the assignment you have (int *) 0x0. Here you tell the compiler, that it should assume a pointer to int which points to the address 0x0.
The full assignment volatile int * p = (int *) 0x0; assigns p the value 0x0. So in total you told the compiler, that at address 0x0 is an integer value which has side effects. It can be access by *p.
volatile
You seem to be unclear what volatile means. Take a look at this code:
int * ptr = (int *) 0x0BADC0DE;
*ptr = 0;
*ptr = 1;
An optimizing compiler would take a short look on this code and say: Setting *ptr to 0 has no effect. So we will skip this line and will only assign 1 immediately to safe some execution time and shrink the binary at the same time.
So effectively the compiler would only compile the following code:
int * ptr = (int *) 0x0BADC0DE;
*ptr = 1;
Normally this is OK, but when we are talking about memory mapped IOs, things get different.
A memory mapped IO is some hardware that can be manipulated by accessing special RAM addresses. A very simple example would be an output -- a simple wire coming out of your processor.
So let's assume, that at the address 0x0BADC0DE is a memory mapped output. And when we write 1 to it, this output is set to high and when we write 0 the output is set to low.
When the compiler skips the first assignment, the output is not changed. But we want to signalize another component something with a rising edge. In this case we want to get rid of the optimization. And one common way to do this, is to use the volatile keyword. It instructs the compiler that it can't check every aspect of read or write accesses to this register.
volatile int * ptr = (int *) 0x0BADC0DE;
*ptr = 0;
*ptr = 1;
Now the compiler will not optimize the accesses to *ptr and we are able to signalize a rising edge to an outside component.
Memory mapped IOs are important in programming embedded systems, operating systems and drivers. Another use case for the volatile keyword would be shared variables in parallel computing (that in addition to the volatile keyword would need some kind of mutex of semaphore to restrict simultaneous access to these variables).