Update 18 February 2017
The answer below quotes & discusses the language in the Standard, some contradictory language in the Rationale and some comments from gnu.cc re the contradiction. There is a defect report which essentially has committee agreement (although still open) that the Standard should say, and that the intent has always been, and that implementations have always reflected, that it is not the volatility of an object that matters (per the Standard) but of the volatility of (the lvalue of) an access (per the Rationale). (Credit to Olaf for mentioning this DR.)
Defect Report Summary for C11 Version 1.10 Date: April 2016 DR 476 volatile semantics for lvalues 04/2016 Open
No. Because the object accessed is not volatile.
Object p
is of type pointer to volatile int. But x
is not an object of a volatile-qualified type. The qualifications on p affect what accesses can be made through it, but do not affect the type of the object that it points to. There is no restriction on accessing a non-qualified-type object via a volatile lvalue. So accessing x through p is not an access of a object of a volatile-qualified type.
(See 6.7.3 Type qualifiers for the restrictions on accessing objects of qualified types. It just says you can't access a volatile qualified object via an unqualified lvalue.)
On the other hand, this post quotes from the 6.7.3 of the Rationale for International Standard--Programming Languages--C:
A cast of a value to a qualified type has no effect; the qualification
(volatile, say) can have no effect on the access since it has occurred
prior to the case. If it is necessary to access a non-volatile object
using volatile semantics, the technique is to cast the address of the
object to the appropriate pointer-to-qualified type, then dereference
that pointer.
However, I can't find language in the standard that says that the semantics is based on the lvalue type. From gnu.org:
One area of confusion is the distinction between objects defined with
volatile types, and volatile lvalues. From the C standard's point of
view, an object defined with a volatile type has externally visible
behavior. You can think of such objects as having little oscilloscope
probes attached to them, so that the user can observe some properties
of accesses to them, just as the user can observe data written to
output files. However, the standard does not make it clear whether
users can observe accesses by volatile lvalues to ordinary objects.
[..] it is not clear from the standard whether volatile lvalues
provide more guarantees in general than nonvolatile lvalues, if the
underlying objects are ordinary.
No, because there are no side effects:
Even if the semantics of *p
must be that of a volatile, the standard nevertheless says:
5.1.2.3 Program execution 4 In the abstract machine, all expressions are
evaluated as specified by the semantics. An actual implementation
need not evaluate part of an expression if it can deduce that its
value is not used and that no needed side effects are produced
(including any caused by calling a function or accessing a volatile
object).
Again, there is no volatile object in your code. Although a compilation unit that only sees p
couldn't make that optimization.
Also keep in mind
6.7.3 Type qualifiers 7 [...] What constitutes an access to an object that has volatile-qualified type is implementation-defined.
5.1.2.3 Program execution 8 More stringent correspondences between abstract and actual semantics may be defined by each implementation.
So mere appearances of volatile lvalues does not tell you what "accesses" there are. You have no right to talk about "the single access to *p
from tmp = *p
" except per documented implementation behaviour.