The main intended use-case for volatile
in C and C++ is to access data that can be modified by something external to the program,
typically some hardware register. In contrast with some other languages with a volatile
keyword, it does not provide any useful
guarantees related to atomicity, memory ordering, or inter-thread synchronization. It is only really needed for the kind of low-level code found in
kernels or embedded software, i.e. using memory-mapped I/O registers to manipulate hardware directly.
According to the C standard:
volatile
is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might
be changed by means undetectable by an implementation.
Local variables and data members are completely controlled by the C++ language. This means they can’t change their value without the compiler
knowing about it. Therefore, it doesn’t make sense for them to be marked as volatile
.
If the intent is to share those variables between threads, race conditions can be avoided by using synchronization primitives (such as
std::mutex
) or atomic types (_Atomic
in C11, std::atomic<T>
in C++11).
This rule raises an issue when a local variable or class data member is declared as volatile
(at the top level of the type, pointers
to volatile are not reported).
Noncompliant code example
volatile int counter; // Noncompliant
User * volatile vpUser; // Noncompliant; pointer is volatile
User volatile * pvUser; // Compliant; User instance is volatile, not the pointer
Compliant solution
atomic_int counter;
std::atomic<User*> vpUser;
User volatile * pvUser;