Many low level programs use the volatile keyword for types for memory mapping and such, however I'm sort of confused as to what it REALLY does in the background. In other words, what does it mean when the compiler doesn't "optimize away" the memory address?
volatile means some other processor or I/O device or something can change the variable out from under you.
With an ordinary variable, your program's steps are the only thing that will change it. So for instance if you read
5 from a variable and you don't change it, it'll still contain
5. Since you can rely on that, your program doesn't have to take the time to read the variable again next time you want to use it. The C++ compiler is smart to generate code that just remembers the
But you could read it as
5, then perhaps the system loads data from disk into that memory, changing it to
500. If you want your program to read the fresh value
500, you need the compiler to not be too smart about using the previously-read
5. You need to tell it to reload the value every time. That's what
An analogy for 5-year olds
Let's say you put a big sheet of paper on a table. In one corner of the paper, you write down the current score of an ongoing game,
3 to 4. Then you go to the opposite side of the table and start writing a story about the game. Your friend who's watching the game updates the score in that corner as the game proceeds. She erases
3 to 4 and writes
3 to 5.
When you go to put the game score into your story, you could either:
- Write down the last score you read,
3 to 4, merrily assuming it didn't change (or not minding if it did), or
- Walk around to the opposite side of the table to read the current score (which happens to be
3 to 5now), and walk back. That's how a
volatile means two things:
The value of the variable may change without any code of yours changing it. Therefore whenever the compiler reads the value of the variable, it may not assume that it is the same as the last time it was read, or that it is the same as the last value stored, but it must be read again.
The act of storing a value to a volatile variable is a "side effect" which can be observed from the outside, so the compiler is not allowed to remove the act of storing a value; for example if two values are stored in a row, then the compiler must actually store the value twice.
As an example:
i = 2; i = i;
The compiler must store the number two, read the variable i, store the variable that it read into i.
There is another situation: If a function uses
setjmp and then
longjmp is called, all volatile local variables of the function are guaranteed to have the last value stored - this is not the case with non-volatile local variables.
Both C and C++ have a concept of an abstract machine. When the code uses the value of some variable, the abstract machine says the implementation has to access the value of that variable. Code of the form
statement_A; statement_B; statement_C; has to be executed in exactly the order specified. Expressions common to those three statements must be re-calculated each time they occur.
Per the abstract machines, given the sequence of statements
statement_A; statement_B; statement_C;, the implementation must first perform
statement_A in its entirety, then
statement_B, and finally
statement_C. The implementation cannot remember that you assigned
age the value of 5 . Every statement that references
age must instead access the value of that variable.
There would be no need for the
volatile keyword if implementations strictly executed C or C++ code per the abstract machine specifications. The C and C++ abstract machines have no concept of registers, no concept of common subexpressions, and execution order is strict.
Both languages also have as-if rules. An implementation is compliant with the standard so long as that implementation behaves as if it had executed things per the abstract machine specification. The compiler can assume non-volatile variables don't change values between assignments. So long as it doesn't break the
as-if rule, the sequence
statement_A; statement_B; statement_C; might be implemented by executing part of
statement_C, then part of
statement_A, then all of
statement_B, then the rest of
statement_A, and finally the rest of
Those as-if rules do not apply to
volatile variables. With regard to
volatile variables and functions, an implementation has to do exactly what you told it to do, and exactly in the order you told it to do things.
There's a downside to the abstract machine specification: It's slow. One positive aspect of C and C++ compared to other languages is that they are quite fast. This would not be the case if code was executed per these abstract machines. The as-if rules are what enable C and C++ to be so fast.
what does it mean when the compiler doesn't "optimize away" the memory address?
"Optimizing away" a memory address is an advanced concept, something that is not within the realm of the capabilities of a five year old. Compliant five year olds will do exactly what you tell them to do, no more, no less. With
volatile, you are telling the implementation to act like it's five: No thinking, no fancy optimizations. Instead, the implementation has to do exactly what the code tells it to do.
(non-)volatile is a hint for the compiler how to optimize code (from generated assembly-code point of view):
- non volatile means that your current compiler decides where the variable will be located or how the variable-s value is teransfered to a subroutine
- in a fixed memory adress,
- on the stack [relative to the processors current stackpointer],
- on the heap [relative to the processors current basepointer],
- in a processor register,
- volatile means that the compiler cannot optimize the variable because something else outside of the main-cpu-s controll (i.e. a seperate io-processer) can change this value.
The answers seem pretty consistent but missing an important point. You are telling the compiler that you want to to allocate space and for every access, read OR WRITE, you want it to perform that access. We dont want it to optimize away those accesses or that variable for some reason.
Yes, one reason is because someone else might change that value for us. Another reason is that we might be changing that value for someone else. That someone else be it the one that changes it for us or the one we are changing it for might be hardware/logic or software. It is often used to define accesses to control and status registers in bare metal embedded programs, writing to or reading from hardware. As well as software talking to software explained in other answers.
You will also see volatile used to control when and in what order the accesses occur, if you are trying to time a section of code, and you dont use volatile the variables in question (beginning time, end time and difference) only need to be computed near the end the compiler is free to move either of the time measurements around (not where we placed them), not that it cant with volatile but experience shows it less likely.
On occasion, you will see it used to simply burn time, an elementary led blinker, the hello world of bare metal, might use a volatile for a variable that counts to some large number just to burn time for the human eye to see the led change state. More advanced examples then using timers or other events to burn the time.