Suppose I have the following declaration:
int* volatile x;
int *volatile x; declares a volatile pointer to a non-volatile int.
Whenever the pointer is accessed, the
volatile qualifier guarantees that its value (the value of the pointer) is re-read from memory.
Since the pointed-to
int is non-volatile, the compiler is allowed to reuse a previously cached value at the address pointed to by the current value of the pointer. Technically this is allowed regardless of whether the pointer has changed or not, as long as there exists a cached value originally retrieved from the current address.
C11 draft N1570 6.7.3/7 says:
An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 22.214.171.124. Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously (134). What constitutes an access to an object that has volatile-qualified type is implementation-defined.
The same draft has a footnote for 6.5.16/3 (assignment operators):
The implementation is permitted to read the object to determine the value but is not required to, even when the object has volatile-qualiﬁed type
So in the end
volatile does not require a physical memory read, but the observable behavior of a compliant implementation must be as if one was made regardless.