Question bank — volatile keyword — preventing optimization of hardware registers
This bank hunts the misconceptions volatile invites: confusing it with atomicity, mis-parsing the pointer slot, thinking -O0 is a substitute, and forgetting hardware/ISR/DMA can change memory behind the compiler's back.
Two figures below give you the pictures the text leans on: a memory-access timeline (why the loop hangs) and a pointer-slot map (where the word volatile binds).


True or false — justify
Each item: decide true/false, then give the why. The reveal always contains the reasoning, never a bare verdict.
volatile guarantees that count++ is safe against interrupts.
count++ is still load–modify–store; an ISR can fire between the load and the store. volatile only forces each access to hit memory — use _Atomic for atomicity.A volatile access is treated by the C standard as an observable side effect.
Compiling at -O0 makes volatile unnecessary for hardware registers.
-O0 only accidentally keeps the re-reads; it is fragile, breaks at -O2, and hides intent. volatile is correct at every optimization level.const and volatile cannot be combined on one variable.
const volatile describes a read-only hardware register: your code never writes it (const) but hardware does (volatile), so it is still re-read each access. See const qualifier.volatile int *p and int *volatile p mean the same thing.
*p); the second makes the pointer variable volatile while the data is ordinary. See Pointers and Type Qualifiers and figure s02.Marking a shared variable volatile inserts a memory barrier between it and your other variables.
volatile orders volatile accesses among themselves but gives no ordering guarantee for ordinary variables — that needs explicit barriers.Two consecutive reads a = *DATA; b = *DATA; on a volatile FIFO pointer both actually execute.
DATA is a pointer to a FIFO's data register and *DATA reads one word from it; each volatile read is a side effect the compiler may not merge, so without volatile it would fold b = a and lose a FIFO word.volatile can slow down a tight loop.
Declaring a variable volatile changes the value the hardware writes into it.
volatile changes nothing about the data itself; it only changes whether the compiler re-reads and preserves accesses.Spot the error
Each line names buggy intent; the reveal explains the actual defect and the fix.
int *volatile p = (int*)0x40000000; used to poll a status register.
p is volatile but *p is cached, so polling still hangs. Want volatile int *p so the target is re-read (see figure s02).volatile int flag; while(flag==0){} — but the ISR does flag = 1; on a non-volatile copy declared elsewhere.
flag correctly, but if the ISR updates a different / non-volatile object they aren't the same memory. Both sides must refer to one shared volatile object.Using volatile on a shared counter and assuming counter += 1 in an ISR and in main won't race.
+= 1 is read–modify–write; a preemption between steps loses an update. Needs atomic ops or interrupt masking, not volatile.#define REG *(uint32_t*)0x4000C000 (no volatile) then polling while(!(REG&1)){}.
volatile, so the compiler reads REG once and may loop forever. Use *(volatile uint32_t*)0x4000C000.Trying to memcpy a large volatile struct expecting each field to be re-read.
memcpy takes non-volatile pointers; the volatile-ness is dropped, so per-element ordering/re-reads aren't guaranteed. Copy field-by-field through volatile accesses.Writing volatile int x; x = compute(); x = compute(); and expecting the first write dropped as dead.
Why questions
Why does the compiler cache a variable in a register by default?
Why is volatile described as "removing an assumption" rather than "adding a feature"?
volatile just forbids the optimizations that skip it.Why must a UART status poll mask with & 0x01 rather than compare the whole register?
Why doesn't turning off optimization document your intent the way volatile does?
-O0 is a global build flag with no connection to a specific variable; volatile states at the declaration that this object changes externally, surviving refactors and higher -O levels.Why is const volatile sensible for a read-only status register?
const stops your code from writing it (catching accidental stores at compile time) while volatile forces re-reads because hardware mutates it — the two qualifiers describe two different actors.Why can Memory-mapped IO break without volatile even when the syntax looks identical to normal memory?
volatile tells the compiler those accesses are meaningful and must not be merged or removed.Edge cases
What does volatile guarantee about a multi-byte read on a 8-bit bus?
volatile read may split into several bus transactions, so a value can tear if hardware/ISR updates mid-sequence. volatile only guarantees each access is real, not indivisible.Is a volatile local variable ever useful?
setjmp/longjmp or an ISR holding a pointer to it); a purely local volatile just pessimizes without protecting anything.What happens to volatile across a function call whose body the compiler can inline?
Does volatile on a pointer target survive when you pass *p (a plain value) into a function?
*p into an ordinary parameter, the read already happened and the copy is a normal non-volatile value. The qualifier protects the access, not the resulting data.Zero-degenerate case: what if a volatile variable is never written by any code at all?
Limiting case: at maximum optimization with whole-program analysis, can the compiler ever drop a volatile access?
Recall One-line summary of the traps
volatile = "re-read this from memory, never cache/drop/reorder its accesses." It is NOT atomicity, NOT a memory barrier for other variables, NOT indivisibility, and its meaning flips with pointer placement.
The block below is a spaced-repetition section: #flashcards/coding tags these Q ::: A lines so the vault's review plugin can surface them on a schedule — they are the two highest-value facts from this whole page, pulled out for daily drilling.