What the C standard says about extern const

245 Views Asked by At

Consider the following program

extern const int foo;
extern void blah(void);

int toto(void) {
  int x = foo;
  blah();
  int y = foo;
  return x + y;
}

arm-linux-gnueabihf-gcc -std=c99 -O2 -fno-pic -S extern_const2.c compiles it to

toto:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    movw    r3, #:lower16:foo
    movt    r3, #:upper16:foo
    push    {r4, lr}
    ldr r4, [r3]
    bl  blah
    lsls    r0, r4, #1
    pop {r4, pc}

Notice that foo is read once then shifted left by 1, which means that gcc assumes its value to remain constant across the function calls. Similar behavior is observed of clang.

I don't see which part of the ISO/IEC 9899:2017 standard clearly discusses this assumption. §6.7.3-12 explains what to assume about aextern volatile const variable (its value can be changed by hardware, but not be assigned to by the program), but this does not apply to extern const.

§6.7.3-6 states that the program may not assign to a const variable. It is not so clear to me that it implies that an external call cannot mutate this variable.

In addition, the meaning of §6.7.3-6 is not so clear to me. It says

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

However, here in my example I declared, not defined, foo as const. The difference between declaration and definition is given in §6.7-5:

A declaration specifies the interpretation and attributes of a set of identifiers. A definition of an identifier is a declaration for that identifier that: — for an object, causes storage to be reserved for that object;

4

There are 4 best solutions below

1
Eric Postpischil On BEST ANSWER

However, here in my example I declared, not defined, foo as const. The difference between declaration and definition is given in §6.7-5:…

The compiler may assume an object declared as const is also defined as const, per C 2018 6.2.7 2:

All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.

and 6.7.3 11:

For two qualified types to be compatible, both shall have the identically qualified version of a compatible type; the order of type qualifiers within a list of specifiers or qualifiers does not affect the specified type.

(Note that that sentence does not say what the requirement is for an unqualified type to be compatible with a qualified type, unless we include “no qualifiers” as a qualified type. However, the rules for compatibility are stated throughout the C standard on an affirmative basis: Two types are compatible only if there is a rule stating they are; any types without a statement asserting they are compatible are not compatible.)

§6.7.3-6 states that the program may not assign to a const variable. It is not so clear to me that it implies that an external call cannot mutate this variable.

Per the above, the compiler may assume that foo is const throughout the program and that nothing in the program changes it. If something outside the program could change it, it should be declared volatile.

4
Antti Haapala -- Слава Україні On

The language definition says only that

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 5.1.2.3.

This is the reason for volatile keyword. If volatile is not specified, C compiler can assume that every function call will conform to the standard and therefore will not change a non-volatile, const-qualified format.

1
KamilCuk On

What the C standard says about extern const

I think I'll throw 6.3.2.1 p1

An lvalue is an expression [...] that potentially designates an object. [...] A modifiable lvalue is an lvalue tha [...] does not have a const-qualified type [...]

foo is designating an object. foo is const-qualified. So it is not modifiable. It can't change.

extern doesn't matter, extern means that the object has external linkage - the definition of the object is somewhere else. Still, the object itself is not modifiable. Means, from the point of the C programming language, the value of the object will not change, nor by any function, nor ever.

0
Luis Colorado On

§6.7.3-6 states that the program may not assign to a const variable. It is not so clear to me that it implies that an external call cannot mutate this variable.

It should. If data is const then the compiler is allowed to cache the data between calls. Const means that the programmer is telling this data is not about to change (or be changed) not just to this compilation unit, but to all of them. If other compilation unit defines this data as non-const, then all of them should declare it equal. Anyway, it is clear, from what you have seen in the compiled assembly, that the compiler assumes the data is not to change, and so, the assumption is that the data will not change and so, the compiler can cache the values.

If you consider the data can be changed externally by other compilation units, you should turn your declaration into

const volatile int foo;

instead, and you should then observe that the compiler will not assume anymore the data has not changed and will not cache its value.