Is accessing a non-const object through a const declaration allowed by the C standard?
E.g. is the following code guaranteed to compile and output 23 and 42 on a standard-conforming platform?
translation unit A:
int a = 23;
void foo(void) { a = 42; }
translation unit B:
#include <stdio.h>
extern volatile const int a;
void foo(void);
int main(void) {
printf("%i\n", a);
foo();
printf("%i\n", a);
return 0;
}
In the ISO/IEC 9899:1999, I just found (6.7.3, paragraph 5):
If an attempt is made to modify an object defined with a const-qualified type through use
of an lvalue with non-const-qualified type, the behavior is undefined.
But in the case above, the object is not defined as const (but just declared).
UPDATE
I finally found it in ISO/IEC 9899:1999.
6.2.7, 2
All declarations that refer to the same object or function shall have compatible type;
otherwise, the behavior is undefined.
6.7.3, 9
For two qualified types to be compatible, both shall have the identically qualified
version of a compatible type; […]
So, it is undefined behaviour.
TU A contains the (only) definition of
a. Soareally is a non-const object, and it can be accessed as such from a function in A with no problems.I’m pretty sure that TU B invokes undefined behavior, since its declaration of
adoesn’t agree with the definition. Best quote I’ve found so far to support that this is UB is 6.7.5/2:[Edit: the questioner has since found the proper reference in the standard, see the question.]
Here, the declaration in B asserts that
ahas typevolatile const int. In fact the object does not have (qualified) typevolatile const int, it has (qualified) typeint. Violation of semantics is UB.In practice what will happen is that TU A will be compiled as if
ais non-const. TU B will be compiled as ifawere avolatile const int, which means it won’t cache the value ofaat all. Thus, I’d expect it to work provided the linker doesn’t notice and object to the mismatched types, because I don’t immediately see how TU B could possibly emit code that goes wrong. However, my lack of imagination is not the same as guaranteed behavior.AFAIK, there’s nothing in the standard to say that
volatileobjects at file scope can’t be stored in a completely different memory bank from other objects, that provides different instructions to read them. The implementation would still have to be capable of reading a normal object through, say, avolatilepointer, so suppose for example that the “normal” load instruction works on “special” objects, and it uses that when reading through a pointer to a volatile-qualified type. But if (as an optimization) the implementation emitted the special instruction for special objects, and the special instruction didn’t work on normal objects, then boom. And I think that’s the programmer’s fault, although I confess I only invented this implementation 2 minutes ago so I can’t be entirely confident that it conforms.