In this example, does correctness require global_value
to be declared volatile
?
int global_value = 0;
void foo () {
++ global_value;
}
void bar () {
some_function (++global_value);
foo ();
some_function (++global_value);
}
My understanding is that volatile
is "intended" for pointers to mapped memory and variables which can be modified by signals (and emphatically not for thread-safety) but it's easy to imagine that bar
might compile to something like this:
push EAX
mov EAX, global_value
inc EAX
push EAX
call some_function
call foo
inc EAX
push EAX
call some_function
mov global_value, EAX
pop EAX
This is clearly not correct, but even without volatile
I believe it is valid according to the C abstract machine. Am I wrong or is it valid?
If so, it seems to me that volatile
is routinely overlooked. This would be nothing new!
void baz (int* i) {
some_function (++*i);
foo ();
some_function (++*i);
}
int main () {
baz (&global_value);
}
Even if bar
is guaranteed to compile into a correct dont-cache-global_value implementation, will baz
be similarly correct, or is it allowed to cache the non-volatile value of *i
?
A variable should be declared volatile whenever its value could change unexpectedly. In practice, only three types of variables could change: Memory-mapped peripheral registers. Global variables modified by an interrupt service routine.
Global variables can be altered by any part of the code, making it difficult to remember or reason about every possible use. A global variable can have no access control. It can not be limited to some parts of the program. Using global variables causes very tight coupling of code.
Non-const global variables are evil because their value can be changed by any function. Using global variables reduces the modularity and flexibility of the program. It is suggested not to use global variables in the program. Instead of using global variables, use local variables in the program.
The volatile keyword in C++11 ISO Standard code is to be used only for hardware access; do not use it for inter-thread communication. For inter-thread communication, use mechanisms such as std::atomic<T> from the C++ Standard Library.
No, the volatile
keyword is not necessary here. Since global_value
is visible outside the function bar
, the compiler must not assume that it remains the same if another function is called.
[Update 2011-07-28] I found a nice citation that proves it all. It's in ISO C99, 5.1.2.3p2, which I am too lazy to copy here in its entirety. It says:
At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place.
Sequence points include:
There you have your proof.
The only uses of volatile
involve longjmp
, signal handlers, memory-mapped device drivers, and writing your own low-level multi-threaded synchronization primitives. For this last use however, volatile
is not sufficient and may not even be necessary. You'll definitely also need asm (or compiler-specific or C1x atomics) for synchronization.
volatile
is not useful for any other purposes, including the code you asked about.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With