In your function, you only access the volatile variable once (and this is the only volatile one in this function), so you donโt have to worry about code reorganization that the compiler can execute (and prevents volatile ). What standard says for these optimizations in 5.1.2.3:
In an abstract machine, all expressions are evaluated according to semantics. An actual implementation should not evaluate part of an expression if it can infer that its value is not used and that the necessary side effects are not created (including those caused by a function call or access to a mutable object).
Pay attention to the last sentence: "... the necessary side effects are not created (... access to the unstable object)."
Just volatile won't let the optimization compiler get around this code. Just to mention a few: no command reorders other volatile variables. without deleting an expression, without caching, without spreading values โโacross functions.
BTW I doubt that any compiler can break your code (with or without volatile ). It is possible that the local variable of the stack will be changed, but the value will be stored in the registry (for sure it will not re-access the memory cell). What you need volatile for is the visibility of values.
EDIT
I think some clarification is needed.
Let me safely assume that you know what you are doing (you work with interrupt handlers, so this should not be your first C program): the processor word matches the type of your variable, and the memory is correctly aligned.
Let me also suggest that your interrupt is not reentrant (some magic cli / sti stuff or something that your processor uses for this), unless you are planning some kind of debugging and custom procedure.
If these assumptions are fulfilled, then you do not need atomic operations . What for? Since localSampleIndex = gSampleIndex is atomic (because it is correctly aligned, the word size is the same and it is volatile ), with ++gSampleIndex there is no race condition ( HandleSomeIrq will no longer be called while it is still running). More than useless , they are mistaken .
You might think: โWell, I might not need atomic, but why can't I use them? Even if such an assumption is fulfilled, it will be * extra *, and it will achieve the same goal.โ No it's not . Atomic does not have the same semantics of volatile variables (and rarely volatile used / should be used for memory I / O and signal processing). Volatile (usually) useless with atomic ones (unless a specific architecture talks about it), but it has a big difference: visibility. When you update gSampleIndex in HandleSomeIrq , the standard ensures that the value is immediately visible to all streams (and devices). with standard atomic_uint ensures that it will be visible for a reasonable amount of time.
To make it short and clear: volatile and atomic are not the same thing. . Atomic operations are useful for concurrency, volatile are useful for lower-level things (interrupts, devices). If you still think โhey, they do exactly what I need,โ please read some useful links selected from the comments: cache coherence and a nice read about atomatics.
Summarizing:
In your case, you can use an atomic variable with a lock (have both atomic access and a visibility value), but no one on this earth would put a lock inside the interrupt handler (if absolutely definitely, no doubt there was no need, and from the code that you sent is not your case).