crisgoogle said:
So what it comes down to is: an object simply is, or is not, volatile,
regardless of how it is declared by the programmer? And if the object
_is_ volatile, but the programmer neglects to declare it as such,
the behaviour is undefined?
That may be so if the object is accessed in some way that puts it firmly
in the implementation's purview but not otherwise.
For example, if we know that a clock counter is available at address
0x400 or by using the external name __clock__ then we should write:
volatile int *count_ptr = (int *)0x400;
or
volatile int __clock__;
The C standard does not care what happens when the volatile keyword is
omitted in either of cases because the behaviour is down to the
implementation. But it does care in this case:
volatile int the_clock;
Without the volatile keyword, the_clock must hold the value the program
put in it or the implementation is not conforming. Names like this
could be used for such a purpose because the implementation can arrange
for the volatile nature of a declaration to be passed on to the linker
(if there is one). Volatile and non-volatile declarations could be
treated quite differently (the obvious way be some form on name
mangling: __the_clock for one and _vol__the_clock for the other).
I think this is, in part, why there has been such a strong feeling
against the notion of a volatile parameter. How can the implementation
preserve the correct meaning for ordinary parameters (i.e. that they
don't change value without explicit stores) and yet have those that are
declared volatile be subject to possible external change.
The same two ways out of the dilemma are available for volatile
parameters: either (a) the implementation is permitted to misbehave when
the keyword is omitted, or (b) there must be collusion between the
implementation and the agent that modifies the volatile objects so that
volatile and non-volatile parameters can be treated differently.
For example, in my start-up examples, the implementation might know that
the initial activation frame is at an address that the hardware maps to
changeable memory locations so the parameters /must/ be declared
volatile. This is an example of (a) above. A programmer who omits the
volatile keyword has already broken the contract with the
implementation.
In the garbage collection example, the implementation could arrange for
the stack to have markers to indicate which function calls have volatile
parameters and which don't. The GC could then scan the stack looking
for pointers that it might have to change. This an example of (b): the
implementation passes on enough information for ordinary parameters to
behave as they should.
Obviously, this meshes with reality: above I referred to an object
that
_ought_ to be volatile, i.e., one that the programmer should define
as volatile in order to get the correct behaviour from the program.
It seems that you're saying this object _is_ volatile, regardless
of the presence or absence of the keyword.
I'm actually happy with this reasoning; like I said, it meshes with
reality. But I don't see in the Standard any definition of volatile
objects independent of the volatile keyword. Most references are
explicitly to "volatile-qualified type(s)", and though there are
a couple of references to "volatile objects", these seem to be
referring in fact to objects of volatile-qualified type, and
certainly are _not_ explicitly referring to "objects that _ought_
to be volatile" -- sorry, I don't have a better-but-short-enough
phrase to describe "objects that may change behind the
implementation's back". =)
I think the standard gets it about right. I don't think there is an
easy solution that transfers the volatility to the object itself and
away from the name used to access it.