> I'm willing to admit that I don't fully understand where volatile goes. Not > only would it be very noble of someone to explain this to us, but I'm > certain that he would be rained with fame, women, and money for his efforts.
It goes like this: the C language is inherently single-threaded. It does not understand multiple threads or interrupt handlers. The standard is very clear that the compiler is allowed to assume that nobody is changing any variables except the executing code. So when I write foo = 42; bar = foo; If I don't refer to foo again, the compiler is quite free to eliminate it entirely and just generate bar = 42; Further, if the rest of the code ends up using the vale of bar/2 a lot, the compiler could just let bar = 21 and eliminate all those divisions. To take a simple example, if you write a standard loop in C: for (i = 0; i < 10; i++) foo(); gcc actually generates: bar: call #foo call #foo call #foo call #foo call #foo call #foo call #foo call #foo call #foo call #foo ret Look ma, no variable i at all! If I make the loop a little bigger: unsigned x = 0 for (i = 0; i < 100; i++) x += foo(x); return x; Then I get bar: push r11 push r10 mov #0, r10 mov #99, r11 .L6: mov r10, r15 call #foo add r15, r10 add #-1, r11 jge .L6 mov r10, r15 pop r10 pop r11 ret The closest thing to a variable i is r11, which counts from 99 down to 0 rather than 0 up to 99. The compiler can do this because it knows that nobody, including the called function foo, is allowed to see or change i, so it can convert it to a different form. This sort of thing is harder to do with global variables, because the compiler can't see the entire program as it's compiling (but there do exist compilers which do such "interprocedural" optimization at link time, so it's not impossible), but the compiler can still load the global variable when it enters a function and store it once when it leaves and not touch it in memory in the mean time. One thing it does particularly often is change the order in which it reads from or writes to such variables. Now, any form of multithreading, including interrupt handlers or hardware peripherals that rewrite status registers, breaks this assumption all over the place. In order to make it possible to write such code, the ANSI C standard includes the "volatile" keyword. "volatile" says "someone else may read or write this variable at any time, so don't get clever. Every time I refer to the variable in my source code, you have to actually go to the given memory address and do an actual load or store." The compiler is still allowed to optimize access to any other variables, but doesn't assume that the "volatile" variable has the same value when loaded as was last stored. If you have a variable that is used by an interrupt handler, you must declare it volatile in the non-interrupt code that reads or writes it, or what the interrupt handler sees and when it sees it won't be well-defined. Likewise, if it's written by the interrupt handler, it has to be volatile or there's no guarantee the main code will see the change. This pessimism, however, comes at a significant performance cost. *Don't* use voltatile anywhere you don't need it. If a "variable" never actually varies, or is only accessed from one thread, leaving volatile off generates smaller and faster code. Local variables, for example, are almost never volatile. Hardware registers act like interrupt handlers, too - the "interrupt handler" just executes on custom hardware rather than in the main processor. One possible analogy is like going to the bathroom in the dark. I can get out of bed and find the bathroom without turning the lights on because I know where everything is, so I don't have to see the furniture to avoid tripping on it. The furniture is where I last left it. But if I live with someone who likes rearranging the furniture, I'd better turn on the lights or I'm going to get in trouble. Here's one example of volatile usage that isn't an interrupt handler, but is an excellent example of its intended use. Sometimes, people like to include identifcation strings (like the famous RCS $Id: ...$ headers) in an executable or ROM image. And there are utilities like "rcsid" or "strings" that search through the executable and pull out those strings. A lot of source files start with static char const rcsid[] = "$Id: file.c,v 1.32 2005/11/04 ..."; It's static so the same name ("rcsid") can be used in each file without the compiler complaining. But some compilers are smart enough to notice that nothing inside file.c actually refers to that array, and since it's static, no other source files can refer to it, so they leave it out! But the whole point of the string is that it's read by something *other* than the program - they're read by something that examines the non-running program's executable. This is exactly what volatile is for. You can write static char const volatile rcsid[] = "$Id: file.c,v 1.32 2005/11/04 ..."; and the compiler knows it's not allowed to delete it just because it can't figure out who's reading it.