Why is volatile
needed in C? What is it used for? What will it do?
volatile
tells the compiler not to optimize anything that has to do with the volatile
variable.
There are at least three common reasons to use it, all involving situations where the value of the variable can change without action from the visible code:
- When you interface with hardware that changes the value itself
- when there's another thread running that also uses the variable
- when there's a signal handler that might change the value of the variable.
Let's say you have a little piece of hardware that is mapped into RAM somewhere and that has two addresses: a command port and a data port:
typedef struct
{
int command;
int data;
int isBusy;
} MyHardwareGadget;
Now you want to send some command:
void SendCommand(MyHardwareGadget* gadget, int command, int data)
{
// wait while the gadget is busy:
while (gadget->isBusy)
{
// do nothing here.
}
// set data first:
gadget->data = data;
// writing the command starts the action:
gadget->command = command;
}
Looks easy, but it can fail because the compiler is free to change the order in which data and commands are written. This would cause our little gadget to issue commands with the previous data-value. Also take a look at the wait while busy loop. That one will be optimized out. The compiler will try to be clever, read the value of isBusy
just once and then go into an infinite loop. That's not what you want.
The way to get around this is to declare the pointer gadget
as volatile
. This way the compiler is forced to do what you wrote. It can't remove the memory assignments, it can't cache variables in registers and it can't change the order of assignments either
This is the correct version:
void SendCommand(volatile MyHardwareGadget* gadget, int command, int data)
{
// wait while the gadget is busy:
while (gadget->isBusy)
{
// do nothing here.
}
// set data first:
gadget->data = data;
// writing the command starts the action:
gadget->command = command;
}
volatile
is used "when you interface with hardware", I would say "when the variable can be altered by something outside of your program". This covers the memory-mapped hardware case as well as shared memory, etc. –
Naked std::atomic
types, which have stronger guarantees. See N2016 for discussion about volatile
and threads (whitepaper from C++11 standardization process). –
Leahy volatile
variables. They don't act as memory barriers. –
Carbine volatile
variables do exist in registers. notably volatile
is indeed useful on memory mapped registers. crazy this answer has 67 upvotes... The next wrong thing is the re-ordering guarantee, please read software.intel.com/en-us/blogs/2007/11/30/… –
Prewitt volatile MyHardwareGadget * gadget
declare the value pointed by gadget is volatile instead of the pointer? or I am wrong? –
Linebreeding std::atomic
to achieve that, or rely on a compiler macro that will implement the hardware-specific memory fence behind the scenes. –
Echinoderm volatile
in C actually came into existence for the purpose of not caching the values of the variable automatically. It will tell the compiler not to cache the value of this variable. So it will generate code to take the value of the given volatile
variable from the main memory every time it encounters it. This mechanism is used because at any time the value can be modified by the OS or any interrupt. So using volatile
will help us accessing the value afresh every time.
volatile
was to make it possible for compilers to optimize code while still allowing programmers to achieve the semantics that would be achieved without such optimizations. The authors of the Standard expected that quality implementations would support whatever semantics were useful given their target platforms and application fields, and didn't expect that compiler writers would seek to offer the lowest quality semantics that conform to the Standard and weren't 100% stupid (note that the authors of the Standard explicitly recognize in the rationale... –
Ganley Another use for volatile
is signal handlers. If you have code like this:
int quit = 0;
while (!quit)
{
/* very small loop which is completely visible to the compiler */
}
The compiler is allowed to notice the loop body does not touch the quit
variable and convert the loop to a while (true)
loop. Even if the quit
variable is set on the signal handler for SIGINT
and SIGTERM
; the compiler has no way to know that.
However, if the quit
variable is declared volatile
, the compiler is forced to load it every time, because it can be modified elsewhere. This is exactly what you want in this situation.
quit
, the compiler can optimise it into a constant loop, assuming that there's no way for quit
to be changed between iterations. N.B.: This isn't necessarily a good substitute for actual threadsafe programming. –
Pillage volatile
or other markers, it will assume that nothing outside the loop modifies that variable once it enters the loop, even if it's a global variable. –
Silicify extern int global; void fn(void) { while (global != 0) { } }
with gcc -O3 -S
and look at the resulting assembly file, on my machine it does movl global(%rip), %eax
; testl %eax, %eax
; je .L1
; .L4: jmp .L4
, that is, an infinite loop if the global is not zero. Then try adding volatile
and see the difference. –
Silicify volatile
tells the compiler that your variable may be changed by other means, than the code that is accessing it. e.g., it may be a I/O-mapped memory location. If this is not specified in such cases, some variable accesses can be optimised, e.g., its contents can be held in a register, and the memory location not read back in again.
See this article by Andrei Alexandrescu, "volatile - Multithreaded Programmer's Best Friend"
The volatile keyword was devised to prevent compiler optimizations that might render code incorrect in the presence of certain asynchronous events. For example, if you declare a primitive variable as volatile, the compiler is not permitted to cache it in a register -- a common optimization that would be disastrous if that variable were shared among multiple threads. So the general rule is, if you have variables of primitive type that must be shared among multiple threads, declare those variables volatile. But you can actually do a lot more with this keyword: you can use it to catch code that is not thread safe, and you can do so at compile time. This article shows how it is done; the solution involves a simple smart pointer that also makes it easy to serialize critical sections of code.
The article applies to both C and C++.
Also see the article "C++ and the Perils of Double-Checked Locking" by Scott Meyers and Andrei Alexandrescu:
So when dealing with some memory locations (e.g. memory mapped ports or memory referenced by ISRs [ Interrupt Service Routines ] ), some optimizations must be suspended. volatile exists for specifying special treatment for such locations, specifically: (1) the content of a volatile variable is "unstable" (can change by means unknown to the compiler), (2) all writes to volatile data are "observable" so they must be executed religiously, and (3) all operations on volatile data are executed in the sequence in which they appear in the source code. The first two rules ensure proper reading and writing. The last one allows implementation of I/O protocols that mix input and output. This is informally what C and C++'s volatile guarantees.
volatile
does not guarantee atomicity. –
C volatile
is not held in a register. In fact the abstract machine allows a lot of optimisations even for volatile
s. –
C volatile
behavior would be necessary to make an implementation suitable for any particular purpose, such as programming a particular real machine. Instead, it expects implementers to recognize the ways in which the real target machine may differ from the abstract one, and thus recognize what semantics users of particular platforms will need and give it to them. On a hardware platform where a load or store may trigger actions that affect program execution, a quality implementation should treat such actions as... –
Ganley My simple explanation is:
In some scenarios, based on the logic or code, the compiler will do optimisation of variables which it thinks do not change. The volatile
keyword prevents a variable being optimised.
For example:
bool usb_interface_flag = 0;
while(usb_interface_flag == 0)
{
// execute logic for the scenario where the USB isn't connected
}
From the above code, the compiler may think usb_interface_flag
is defined as 0, and that in the while loop it will be zero forever. After optimisation, the compiler will treat it as while(true)
all the time, resulting in an infinite loop.
To avoid these kinds of scenarios, we declare the flag as volatile, we are telling to compiler that this value may be changed by an external interface or other module of program, i.e., please don't optimise it. That's the use case for volatile.
A marginal use for volatile is the following. Say you want to compute the numerical derivative of a function f
:
double der_f(double x)
{
static const double h = 1e-3;
return (f(x + h) - f(x)) / h;
}
The problem is that x+h-x
is generally not equal to h
due to roundoff errors. Think about it : when you substract very close numbers, you lose a lot of significant digits which can ruin the computation of the derivative (think 1.00001 - 1). A possible workaround could be
double der_f2(double x)
{
static const double h = 1e-3;
double hh = x + h - x;
return (f(x + hh) - f(x)) / hh;
}
but depending on your platform and compiler switches, the second line of that function may be wiped out by a aggressively optimizing compiler. So you write instead
volatile double hh = x + h;
hh -= x;
to force the compiler to read the memory location containing hh, forfeiting an eventual optimization opportunity.
h
or hh
in derivative formula? When hh
is computed the last formula uses it like the first one, with no difference. Maybe it should be (f(x+h) - f(x))/hh
? –
Slaw h
and hh
is that hh
is truncated to some negative power of two by the operation x + h - x
. In this case, x + hh
and x
differ exactly by hh
. You can also take your formula, it will give the same result, since x + h
and x + hh
are equal (it is the denominator which is important here). –
Engenia x1=x+h; d = (f(x1)-f(x))/(x1-x)
? without using the volatile. –
Slaw -ffast-math
or equivalent. –
Engenia There are two uses. These are specially used more often in embedded development.
The compiler will not optimise the functions that use variables that are defined with the volatile keyword
Volatile is used to access exact memory locations in RAM, ROM, etc... This is used more often to control memory-mapped devices, access CPU registers and locate specific memory locations.
See examples with assembly listing. Re: Usage of C "volatile" Keyword in Embedded Development
volatile
can be used to force memory reads or writes to be a particular size. For example, there can be 8-bit only memory, or 16-bit only memory, and a read or write of the wrong type will have an undesirable effect. Without volatile
, the compiler is free to combine four 8-bit writes into a 32-bit write, even if the memory does not support that kind of write. There are even cases where a for
loop gets changed into memset
which attempts to write bytes to 16-bit only memory, but this disappears when using volatile
. –
Gentry I'll mention another scenario where volatiles are important.
Suppose you memory-map a file for faster I/O and that file can change behind the scenes (e.g. the file is not on your local hard drive, but is instead served over the network by another computer).
If you access the memory-mapped file's data through pointers to non-volatile objects (at the source code level), then the code generated by the compiler can fetch the same data multiple times without you being aware of it.
If that data happens to change, your program may become using two or more different versions of the data and get into an inconsistent state. This can lead not only to logically incorrect behavior of the program but also to exploitable security holes in it if it processes untrusted files or files from untrusted locations.
If you care about security, and you should, this is an important scenario to consider.
Volatile is also useful, when you want to force the compiler not to optimize a specific code sequence (e.g. for writing a micro-benchmark).
In my opinion, you should not expect too much from volatile
. To illustrate, look at the example in Nils Pipenbrinck's highly-voted answer.
I would say, his example is not suitable for volatile
. volatile
is only used to:
prevent compiler optimizations that would normally be useful and desirable. It is nothing about thread safety, atomic access or even memory order.
In that example:
void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
{
// Wait while the gadget is busy:
while (gadget->isbusy)
{
// Do nothing here.
}
// Set data first:
gadget->data = data;
// Writing the command starts the action:
gadget->command = command;
}
The gadget->data = data
before gadget->command = command
only is only guaranteed in the compiled code by the compiler.
At run time, the processor may still reorder the data and command assignment, depending on the processor architecture. The hardware could get the wrong data (suppose a gadget is mapped to hardware I/O). A memory barrier is needed between data and command assignment.
volatile
is degrading the performance for no reason. As for whether it is sufficient, that will depend upon other aspects of the system that the programmer may know more about than the compiler. On the other hand, if a processor guarantees that an instruction to write to a certain address will flush the CPU cache but a compiler provided no way to flush register-cached variables the CPU knows nothing about, flushing the cache would be useless. –
Ganley In the language designed by Dennis Ritchie, every access to any object, other than automatic objects whose address had not been taken, would behave as though it computed the address of the object and then read or wrote the storage at that address. This made the language very powerful, but severely limited optimization opportunities.
While it might have been possible to forbid compilers from assuming that addressable objects would never be changed in weird ways, such an assumption would have been appropriate and useful for the vast majority of objects in C programs, and it would have been impractical to add a qualifier to all the objects for which such assumption would be appropriate. On the other hand, some programs need to use some objects for which such an assumption would not hold. To resolve this issue, the Standard says that compilers may assume that objects which are not declared volatile
will not have their value observed or changed in ways that are outside the compiler's control, or would be outside a reasonable compiler's understanding.
Because various platforms may have different ways in which objects could be observed or modified outside a compiler's control, it is appropriate that quality compilers for those platforms should differ in their exact handling of volatile
semantics. Unfortunately, because the Standard failed to suggest that quality compilers intended for low-level programming on any particular platform should handle volatile
in a way that will recognize any and all relevant effects of a particular read/write operation on that platform, many compilers fall short of doing so in ways that make it harder to process things like background I/O in a way which is efficient but can't be broken by compiler "optimizations".
volatile means the storage is likely to change at any time and be changed by something outside the control of the user program.
This means that if you reference the variable, the program should always check the physical address (i.e., a mapped input FIFO), and not use it in a cached way.
In simple terms, it tells the compiler not to do any optimisation on a particular variable. Variables which are mapped to device register are modified indirectly by the device. In this case, volatile must be used.
Wikipedia says everything about volatile
:
And the Linux kernel's documentation also makes a excellent note about volatile
:
A volatile can be changed from outside the compiled code (for example, a program may map a volatile variable to a memory mapped register).
The compiler won't apply certain optimizations to code that handles a volatile variable. For example, it won't load it into a register without writing it to memory. This is important when dealing with hardware registers.
Volatile is often misunderstood as "disabling optimizations," synchronizing access to a variable, or generating memory fences. None are entirely the case. All it does is disable any compiler optimizations that assume the variable has not changed from an outside context.
What Volatile Does According to GCC
Eliminating Opimizations Effect
To understand volatile
, we can simply look at the code generated by GCC. In the first example we will multiply the volatile variable with argc and store in i
.
volatile int i = 0;
int main(int argc, char **argv)
{
i *= argc;
return 0;
}
See assembly here with O2: https://godbolt.org/z/cf4KGjxvP
main:
mov eax, DWORD PTR i[rip]
add eax, 1
mov DWORD PTR i[rip], eax
xor eax, eax
ret
i:
.zero 4
Volatile tells the compiler that the variable cannot be expected at compile time to have remained the same between accesses. It could change at any point during execution. The compiler is told to disable dead store elimination since doing so could lead to unexpected behavior.
Referencing the variable without doing anything to it also generates a load. https://godbolt.org/z/z9cffTsqb
Basically, the compiler disables any optimization that eliminates loads or stores.
Is It A Fence? On GCC, Yes.
On GCC, volatile appears to act as a compiler fence. This code:
volatile int i,j,k,l;
int main(int argc, char **argv)
{
i += j + k + l;
}
When the order of the addition is changed, the corresponding assembly has a different load order. See: https://godbolt.org/z/z9KG3PnbE
So on GCC, volatile is a compiler memory fence, but no fence instructions are generated to serialize access.
On MSVC, the same sample was generated with the loads in reverse, but changing the variable order did also affect the load order. MSVC also regards it as a compiler fence. https://godbolt.org/z/snWz6fYa6
What Volatile Does Not Do
Volatile is not a memory fence. The compiler could treat it as one, but there is no guarantee. It is free to reorder memory accesses as long as it does not eliminate them. That means if you really need a memory access to happen between two function calls, it may not be generated that way.
Instead, a compiler or real memory fence must be explicitly used. Concurrent access between multiple processors may not work unless controlled with a lock or fence.
Volatile does not ensure atomicity. The loads and stores will happen with single instructions, but atomic operations may not be used. In the first example, the number was incremented with a single operation.
Conclusion
Volatile provides very few guarantees about the behavior of the variable. It should be considered non-portable for any situation outside its very narrow scope. Volatile variable access should be encapsulated inside helper functions and volatile function parameters or returns should be avoided.
The situations where volatile should be used are:
- Memory mapped IO registers
- Signal handler variables
- Interrupt service routine variables (for example, waiting in a loop until device gives "ready" signal)
- Testing the effectiveness of optimization on a single variable
Multithreaded programming should never include volatile since acquiring a critical section for a variable ensures that within it all optimizations are allowed because nothing else can access the variable.
As rightly suggested by many here, the volatile keyword's popular use is to skip the optimisation of the volatile variable.
The best advantage that comes to mind, and worth mentioning after reading about volatile is -- to prevent rolling back of the variable in case of a longjmp
. A non-local jump.
What does this mean?
It simply means that the last value will be retained after you do stack unwinding, to return to some previous stack frame; typically in case of some erroneous scenario.
Since it'd be out of scope of this question, I am not going into details of setjmp/longjmp
here, but it's worth reading about it; and how the volatility feature can be used to retain the last value.
It does not allow the compiler to automatically change values of variables. A volatile variable is for dynamic use.
© 2022 - 2025 — McMap. All rights reserved.