Volatile is a keyword in C language that explicitly tells the compiler that this variable can be modified from outside of program scope and thus prevent the compiler from optimizing that variable.
The first question is who can modify it, so the answer is for example, by the operating system, by another thread of execution such as an interrupt routine or signal handler, or by hardware, anyone.
Now another question what kind of optimization?
For Example, whenever a variable is used frequently this leads to frequently read/write operation at that memory location and this results to lower performance due to time[Memory read/write operation cost ] and In such cases compiler cache its value in a register to avoid memory access and prevents lower performance.
This is a good approach for higher performance, now the problem is when another program/OS wants to access fresh value from that memory location they will never get it because the compile has optimized that variable and placed it to register.
Therefore declaring a variable as volatile tells the compiler that a specific type of behavior is intended, and that such code must not be optimized in such a way that it removes the intended functionality.Therefore declaring a variable as volatile tells the compiler that a specific type of behavior is intended and that such code must not be optimized in such a way that it removes the intended functionality.
Let’s See the hidden picture
//Nonvolatile version of buffer loop
int buffer_full;
int read_stream(void)
{
int count = 0;
while (!buffer_full)
{
count++;
}
return count;
}
//Volatile version of buffer loop
volatile int buffer_full;
int read_stream(void)
{
int count = 0;
while (!buffer_full)
{
count++;
}
return count;
}
The use of the volatile is stated in the two sample routines above. Both of these routines loop reading a buffer until a status flag buffer_full is true.
Their machine code is produced by the compiler for each of the samples, where the C code for each has been compiled using Optimization level two (O2).
//Nonvolatile version of buffer loop
read_stream PROC
LDR r1, |L1.28|
MOV r0, #0
LDR r1, [r1, #0]
|L1.12|
CMP r1, #0
ADDEQ r0, r0, #1
BEQ |L1.12| ; infinite loop
BX lr
ENDP
|L1.28|
DCD ||.data||
AREA ||.data||, DATA, ALIGN=2
buffer_full
DCD 0x00000000
//Volatile version of buffer loop
read_stream PROC
LDR r1, |L1.28|
MOV r0, #0
|L1.8|
LDR r2, [r1, #0]; ; buffer_full
CMP r2, #0
ADDEQ r0, r0, #1
BEQ |L1.8|
BX lr
ENDP
|L1.28|
DCD ||.data||
AREA ||.data||, DATA, ALIGN=2
buffer_full
DCD 0x00000000
In the disassembly of the nonvolatile form of the buffer loop, the line LDR r0, [r0, #0] loads the value of buffer_full into register r0 outside the loop labeled |L1.12|. Because buffer_full is not declared as volatile, the compiler assumes that its value cannot be modified outside the program. Having already read the value of buffer_full into r0, the compiler omits to reload the variable when optimizations are enabled because its value cannot change. The result is the infinite loop labeled |L1.12|.
In the disassembly of the volatile form of the buffer loop, the compiler assumes the value of buffer_full can change outside the program and performs no optimizations, and the value of buffer_full is loaded into the register r0 inside the loop labeled |L1.8| each time.
In coding practice, we must declare a variable as volatile in the following cases:
- To access memory-mapped peripherals.
- Sharing global variables between multiple threads.
- Accessing global variables in an interrupt routine or signal handler.
The compiler never optimize the variables you have declared as volatile.
