volatile Qualifier in C++
Last Updated :
05 Jul, 2024
volatile keyword in C++ is used to declare variables that can be modified by external factors outside the program's control. This qualifier informs the compiler that the variable's value may change anytime, preventing certain optimizations that could lead to unexpected behavior. In this article, we will learn about the volatile qualifier and its usage in various scenarios.
Volatile Variables
In C++ the volatile keyword is used to declare a variable whose value can be changed by various external factors that are outside the program's control, such as hardware or a concurrently executing thread. This prevents the compiler from doing some optimizations assuming the variable value remains unchanged unless explicitly changed within the program. Following is the syntax to use the volatile keyword in C++.
Syntax to Define Volatile Variable
volatile type variable_Name;
Here,
- type: denotes the data type of the volatile variable
- variable_Name: denotes the name of the volatile variable.
Volatile Variables in Functions
Volatile variables within a function is very useful when we deal with hardware registers or shared memory in a multithreaded environment and handle concurrent operations. Volatile variables helps to maintain data consistency and also ensures that the compiler doesn't waste the computer power in optimizations for the variables as the value of the variable can change any time.
Example
The following program demonstrates the implementation of the above concept in C++.
C++
// C++ Program for using volatile variables in functions
#include <chrono>
#include <iostream>
#include <thread>
using namespace std;
// declare the volatile variable
volatile bool interruptFlag = false;
void interruptHandler() { interruptFlag = true; }
void mainLoop()
{
while (!interruptFlag) {
cout << "Working..." << endl;
this_thread::sleep_for(chrono::milliseconds(500));
}
cout << "Interrupt received. Exiting." << endl;
}
int main()
{
thread worker(mainLoop);
this_thread::sleep_for(chrono::seconds(2));
interruptHandler();
worker.join();
return 0;
}
Output
Working...
Working...
Working...
Interrupt received. Exiting.
Explanation: In the above program, the interruptFlag is declared volatile because it is shared between the main thread and the worker thread and it's value can change anytime. The volatile qualifier ensures that the worker thread always reads the most up to date value of the interruptFlag even if the compiler optimizes the read operation.
Volatile Member Variables in a Class
Volatile variables are used within a class when a class represents an hardware interface or when we deal with shared data in the multithreaded environments.
Example
The following program demonstrates the implementation of the above concept in C++.
C++
// C++ Program for using volatile variables in classes
#include <chrono>
#include <iostream>
#include <thread>
using namespace std;
class SensorInterface {
private:
volatile int temperature;
public:
SensorInterface()
: temperature(0)
{
}
void readSensor()
{
// Simulate reading from a sensor
while (true) {
temperature = rand() % 100;
this_thread::sleep_for(chrono::seconds(1));
}
}
int getTemperature() const { return temperature; }
};
int main()
{
SensorInterface sensor;
thread sensorThread(&SensorInterface::readSensor,
&sensor);
for (int i = 0; i < 5; ++i) {
cout << "Current temperature: "
<< sensor.getTemperature() << "°C" << endl;
this_thread::sleep_for(chrono::seconds(1));
}
sensorThread.detach();
return 0;
}
Output
Current temperature: 86°C
Current temperature: 77°C
Current temperature: 15°C
Current temperature: 93°C
Explanation: In the above program the temperature member variable of the class sensorInterface is declared volatile because it is updated in a separate thread. The volatile keyword will ensure that everytime getTemperature() function is called, it reads the latest value from the memory rather than using a cached value stored by the compiler for optimizations.
Volatile Pointers
Just like variables the value represented by the pointers can also change unexpectedly or there can be scenarios where the pointers points to a volatile data. In such scenarios, it is recommended to use volatile pointers so that the pointers always points to the most recent value of the data. Following is the syntax to use volatile pointers in C++.
Syntax to Define Volatile Pointers
There are 3 ways to declare volatile pointers in C++:
1. Pointer to volatile variable
volatile data_type* ptr = & volatile_variable.
2. Volatile pointer to non volatile data
data_type* volatile = &variable_name
3. Volatile pointer to volatile data
volatile data_type* volatile ptr = &volatile_variable
Here,
- data_type: It represents the type of the data pointed by the pointer.
- variable_name: It is the name of non volatile variable.
- volatile_variable: It is the name of the volatile vairable.
Example
The following program demonstrates the implementation of the above concept in C++.
C++
// C++ Program for using volatile pointes
#include <iostream>
using namespace std;
int main()
{
int regular_var = 10;
volatile int volatile_var = 20;
// Pointer to volatile data
volatile int* ptr_to_volatile = &volatile_var;
// Volatile pointer to non-volatile data
int* volatile volatile_ptr = ®ular_var;
// Volatile pointer to volatile data
volatile int* volatile volatile_ptr_to_volatile
= &volatile_var;
// Usage
*ptr_to_volatile = 30;
cout << "volatile_var: " << volatile_var << endl;
*volatile_ptr = 40;
cout << "regular_var: " << regular_var << endl;
*volatile_ptr_to_volatile = 50;
cout << "volatile_var: " << volatile_var << endl;
return 0;
}
Outputvolatile_var: 30
regular_var: 40
volatile_var: 50
Explanation: The following program demonstrates the three ways of using volatile pointers. ptr_to_volatile ensures that the data it points to is always read from memory. volatile_ptr ensures that the pointer itself is always read from memory. volatile_ptr_to_volatile combines both effects, ensuring that both the pointer and the data it points to are always read from memory and gets the most recent value not the cached value stored by the compiler for optimization.
Volatile Member Functions
We can also declare the member functions within a class as volatile so that the compiler does not optimize the function calls away. The volatile member functions indicates that the function may be called on volatile objects and doesn't modify the object state.
Example
The following program demonstrates the implementation of the above concept in C++.
C++
// C++ Program for using volatile member functions
#include <chrono>
#include <iostream>
#include <thread>
using namespace std;
class SafeCounter {
private:
volatile int count;
public:
SafeCounter()
: count(0)
{
}
void increment() volatile { ++count; }
int getValue() const volatile { return count; }
};
void incrementer(SafeCounter& counter)
{
for (int i = 0; i < 1000000; ++i) {
counter.increment();
}
}
int main()
{
SafeCounter counter;
thread t1(incrementer, ref(counter));
thread t2(incrementer, ref(counter));
t1.join();
t2.join();
cout << "Final count: " << counter.getValue() << endl;
return 0;
}
Output
Final count: 2000000
Explanation: In the above example, the increment() and the getvalue() functions are declared as volatile member functions. This allows them to be called on volatile objects from the safeCounter class. The volatile qualifier on these functions ensures that they always work with the most up-to-date value of count, even in a multithreaded environment.
Volatile Qualifier in Multithreaded Environments
In a multithreaded environment when multiple threads access a shared variable concurrently, there's a potential risk of race conditions and data inconsistencies. By declaring shared variables to be volatile, we can ensure that each thread reads the most recent value from memory and writes it from other threads immediately.
Example
The following program demonstrates the use of volatile qualifier in a multithreaded environment.
C++
// C++ Program using Volatile Qualifier in Multithreaded
// Environments
#include <chrono>
#include <iostream>
#include <thread>
using namespace std;
// Declare volatile variable
volatile bool data_ready = false;
void producer()
{
// Simulate data production
this_thread::sleep_for(chrono::seconds(1));
cout << "Producer: Data produced." << endl;
// Signal data is ready
data_ready = true;
}
void consumer()
{
// Wait for data to become ready
while (!data_ready) {
// Spin-wait until data is ready (not recommended in
// real scenarios)
}
// Once data is ready, consume it
cout << "Consumer: Data consumed." << endl;
}
int main()
{
// Create threads
thread t1(producer);
thread t2(consumer);
// Join threads
t1.join();
t2.join();
// Print completion message
cout << "Main: All threads have finished execution."
<< endl;
return 0;
}
Output
Producer: Data produced.
Consumer: Data consumed.
Main: All threads have finished execution.
Time Complexity: O(1)
Auxiliary Space: O(1)
Explanation: The above program is a solution of classical synchronization problem known as the producer consumer problem. In this program, volatile qualifier bool data_ready ensures that the consumer thread reliably observes changes made by the producer thread to data_ready variable. This prevents compiler optimizations that could lead to stale reads, highlighting volatile's crucial role in maintaining synchronization and correct behavior in multithreaded environments.
Applications of Volatile Qualifier in C++
Following are some common applications of the volatile qualifier:
- Real-time Programming: volatile qualifier is used to ensure immediate response to asynchronous external events without compiler optimizations in real time programming.
- Hardware Interfacing: volatile ensures correct access to hardware registers and memory-mapped I/O in embedded systems.
- Multithreaded Environments: volatile helps to ensure the visibility of shared variables across threads, although additional synchronization mechanisms are often necessary.Asynchronous Event Flags: volatile flags indicate state changes from asynchronous events, ensuring the program reacts correctly.
- Memory-Mapped Structures: volatile qualfier guarantees correct access to shared data structures across different execution contexts or processes.
Similar Reads
Type Qualifiers in C++
In C++, type qualifiers are keywords that modify the properties of data types, influencing how variables, pointers, or references can be used. These qualifiers enhance variable declarations by providing additional information on their access and usage constraints, ensuring more controlled and secure
5 min read
How to Use the Volatile Keyword in C++?
In C++, the volatile keyword is used to tell the compiler that the value of the variable declared using volatile may change at any time. In this article, we will learn how to use the volatile keyword in C++. Volatile Keyword in C++We can use the volatile keyword for different purposes like declaring
2 min read
Logging System in C++
The logging system is a very critical component to track how the application behaves, find the problems, and understand the performance of the system. We can create a simple also very effective logging system in C++ to capture and record various events and data that occur during the execution of a p
3 min read
Reflection in C++
Reflection in C++ is defined as the ability of a program to examine and modify its structure and behavior at runtime. This powerful feature enables dynamic code generation, introspection, and metaprogramming. While C++ does not have built-in reflection capabilities like other languages, it offers te
5 min read
Swap Two Numbers Without Third Variable in C++
In C++, swapping two numbers means we need to exchange the value of two numbers. In this article, we will learn how to swap two numbers without using the third variable in C++. Example Input: a=10b=20Output:After swapping:a=20b=10Swap Two Numbers Without Using a Third VariableIn C++ we can swap two
2 min read
How to Release Memory in C++?
In C++, releasing memory means deallocating the memory that was previously allocated by the user. It is very important to avoid memory leaks. Other programming languages like Java support automatic garbage collection to release the dynamically allocated memory, but in C++ we have to release the allo
2 min read
Dangling Pointers in C++
In C++, pointers can be used for various purposes such as storing the address of a variable, allocated objects on the heap, passing functions to other functions, iterating over elements in arrays, and so on. But many problems arise when pointers are not handled properly. In this article, we will lea
6 min read
Immediate Functions in C++
In this article, we will discuss the immediate function used in C++. Immediate Function: In C++20, an immediate function is a function where every call to the function either directly or indirectly produces a compile-time constant expression.These functions are declared by using a consteval keyword
4 min read
Overloading of function-call operator in C++
In this article, we will discuss the Overloading of the function-call operators in C++. The function call operator is denoted by â()â which is used to call function and pass parameters. It is overloaded by the instance of the class known as a function object.When the function call operator is overlo
3 min read
Assigning function to a variable in C++
In C++, assigning a function to a variable and using that variable for calling the function as many times as the user wants, increases the code reusability. Below is the syntax for the same: Syntax: C/C++ Code // Syntax: // Below function is assigned to // the variable fun auto fun = [&]() { cou
2 min read