C - Access to const declaration without constant

Access to an object not const through a const declaration permitted by the C standard? For instance. Is the following code guaranteed to compile and output 23 and 42 on a standard compatible 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 ISO / IEC 9899: 1999, I just found (6.7.3, clause 5):

If an attempt is made to modify an object defined using the const type using an lvalue with a non-constant qualification type, the behavior is undefined.

But in the above case, the object is not defined as const (but just declared).

UPDATE

Finally, I found it in ISO / IEC 9899: 1999.

6.2.7, 2

All declarations related to the same object or function must be of a compatible type; otherwise, the behavior is undefined.

6.7.3, 9

For two qualified types that must be compatible, both must have the same qualification version of the compatible type; [...]

So it works undefined.

+6
source share
5 answers

TU A contains (only) the definition of a . Thus, a really is a non-constant object, and it can be accessed as such from function in without problems.

I am sure that TU B causes undefined behavior, since its declaration a not consistent with the definition. The best quote I have found so far to confirm that this is UB 6.7.5 / 2:

Each declarator declares one identifier and states that when an operand of the same form as the declarator appears in the expression, it denotes a function or object with the volume, duration of storage, and the type indicated by the specification of the declaration.

[Edit: since the questionnaire found the correct link in the standard, see the question.]

Here the declaration in B claims that a is of type volatile const int . In fact, the object does not have a (qualified) type volatile const int , it has a (qualified) type int . Violation of semantics - UB.

In practice, all that happens is that TU A will be compiled as if a not const. TU B will compile as if a were volatile const int , which means that it will not cache the value of a at all. Thus, I expect it to work if the linker does not notice and does not mind the inappropriate types, because I do not immediately see how TU B can emit code that goes wrong. However, my lack of imagination is not the same as guaranteed behavior.

AFAIK, there is nothing in the standard to say that volatile objects in a file area cannot be stored in a completely different memory bank from other objects, which provides different instructions for reading them. An implementation should still be able to read a normal object using, say, a volatile pointer, so suppose, for example, that a "normal" load command works on "special" objects, and it uses this when reading through a pointer to an unstable type. But if (as an optimization) the implementation has issued a special instruction for special objects, and a special instruction did not work on ordinary objects, then an arrow. And I think the programmer’s mistake, although I admit that I only invented this implementation 2 minutes ago, so I can’t be completely sure that it matches.

+5
source

In the translation block B const , the modification of the variable a inside the translation unit B itself will be prohibited.

Changes to this value externally (other translation units) will reflect the value that you see in B.

This is more of a linker problem than a language problem. The compiler can come to terms with the different qualifications of the character a (if there is such information in the object files) when combining compiled translation units.

Note that if it is the other way around ( const int a = 23 in and extern int a in B), you will probably encounter a memory access violation if you try to change a from B, since a can be placed in a read-only area process, usually displayed directly from the .rodata section of the executable.

+3
source

An initialization declaration is a definition, so your object is really not a const qualified object, and foo has all the rights to change it.

In B, you provide access to this object, which has the additional qualification const . Since types ( const qualified version and unqualified version) have the same representation of an object, read access through this identifier is valid.

However, your second printf has a problem. Since you did not define your version of B a as volatile , you are not guaranteed to see a modification of a . The compiler is allowed to optimize and reuse the previous value that it could store in the register.

+1
source

Declaring it as const means that the instance is defined as const. You cannot access it from non-const. Most compilers do not allow this, and the standard says that it is also not allowed.

0
source

FWIW: It is written in H & S5 (section 4.4.3 "Type qualifiers", page 89): "When used in a context for which a value is required, not a designation, qualifiers are excluded from the type." Thus, const has the effect when someone is trying to write something to a variable. In this case, printf uses a as rvalue, and the added volatile (unnecessary IMHO) forces the program to read the variable again, so, I would say, the program should produce OP saw output initially on all platforms / compilers. I will look at the standard and add it if / when I find something new.

EDIT: I could not find a specific solution to this issue in the standard (I used the last draft for C1X ), since all links to linker behavior concentrate on identical names. Typical classifiers for external ads do not seem to be covered. Maybe we should refer this matter to Standardization Committee C.

0
source

Source: https://habr.com/ru/post/901027/


All Articles