The volatile
keyword tells the compiler that the value of the variable may change at any time as a result of external conditions, not only as a result of program control flow.
The compiler will not optimize anything that has to do with the volatile variable.
volatile int foo; /* Different ways to declare a volatile variable */
int volatile foo;
volatile uint8_t * pReg; /* Pointers to volatile variable */
uint8_t volatile * pReg;
There are two main reasons to uses volatile variables:
Let's see this example:
int quit = false;
void main()
{
...
while (!quit) {
// Do something that does not modify the quit variable
}
...
}
void interrupt_handler(void)
{
quit = true;
}
The compiler is allowed to notice the while loop does not modify the quit
variable and convert the loop to a endless while (true)
loop. Even if the quit
variable is set on the signal handler for SIGINT
and SIGTERM
, the compiler does not know that.
Declaring quit
as volatile
will tell the compiler to not optimize the loop and the problem will be solved.
The same problem happens when accessing hardware, as we see in this example:
uint8_t * pReg = (uint8_t *) 0x1717;
// Wait for register to become non-zero
while (*pReg == 0) { } // Do something else
The behavior of the optimizer is to read the variable's value once, there is no need to reread it, since the value will always be the same. So we end up with an infinite loop. To force the compiler to do what we want, we modify the declaration to:
uint8_t volatile * pReg = (uint8_t volatile *) 0x1717;