Language: EN

cpp-variables-volatile

Volatile Variables in C++

In C++, the volatile keyword is a type modifier that tells the compiler that the value of a variable can change at any time (and without the compiler being able to foresee it).

The compiler, when optimizing the code, assumes that the value of a variable does not change outside of the instructions that directly affect it in the source code (which are the ones the compiler controls).

This can lead it to make certain optimizations, such as caching the value of the variable or eliminating unnecessary accesses.

In a variable declared with the volatile modifier, we are telling the compiler that it should not perform certain optimizations when working with that variable.

This is useful when the value of the variable can change unexpectedly. For example due to:

  • Hardware Access: When interacting directly with hardware registers, the value of these registers can change at any time due to external events.
  • Multithreading: In multithreaded applications, a variable shared among threads can be modified by one of the threads at any time.
  • Interrupts: Interrupts can also unexpectedly alter the value of a variable.

How to use volatile

To declare a variable as volatile, simply add the volatile modifier in the variable declaration:

volatile int myVar;

In this declaration, myVar is a variable that can be modified at any time by factors external to its use in the code.

Example of use in hardware access

A typical use case for volatile is when interacting with hardware registers in embedded system programming. Let’s see it with a simple example:

Suppose we have a machine that has a while() loop to wait until someone presses a button.

#include <iostream>

// Variable simulating a hardware register (modified by external hardware)
volatile bool buttonPressed = false;

void checkButton() {
    while (!buttonPressed) {
        // Active wait: the program stays here until the button is pressed
    }
    std::cout << "Button pressed!" << std::endl;
}

int main() {
    std::cout << "Waiting for the button to be pressed..." << std::endl;

    // Simulate that the hardware presses the button after some time
    buttonPressed = true;

    checkButton();

    return 0;
}
  • Without volatile: The compiler could optimize the loop while (!buttonPressed) assuming that buttonPressed never changes, because it sees no modification in the main program (This would cause the program to never exit the loop).
  • With volatile: The compiler always checks the actual value of buttonPressed from memory, ensuring it detects changes made by external hardware.

Limitations and considerations

The volatile modifier has been supported by C++ standards since its early versions and remains relevant in hardware-level programming and embedded systems.

However, in modern applications, especially in multithreaded programming, std::atomic (and other synchronization primitives) are usually preferred to handle shared variables and safe synchronization between threads.