7

In this answer I basically needed static int n = -1; inside a function. I wanted to avoid magic numbers all over the place so I used this instead:

double f(int i)
{
    static const int sentinel = -1;
    static int n = sentinel;

    if (n == sentinel)
    // ...
}

It was however pointed to me that this is not conformant to standard because sentinel is not a (compile time) constant.

This makes sense to me since I know constant integers were made usable in compile time expressions (e.g. size of arrays) in C++. However gcc, clang and icc >v16 compile this code without any warning. Only icc <=v16 and MSVC give this warning/error (see on godbolt).

What does the C Standard say? Does this change between various versions of the standard (c90, c99, c11)? If this is not conformant, can we get a warning on gcc and clang? If it is conformant why do old icc and MSVC give errors?

bolov
  • 72,283
  • 15
  • 145
  • 224
  • 1
    6.6p10: "An implementation may accept other forms of constant expressions." I don't remember when that was introduced, but I'm pretty sure it was in C99. – rici Apr 05 '20 at 02:04
  • 5
    If you consider `-1` to be a magic number, it's *still* a magic number whether you initialise `n` *or* `sentinel` with it. Magic numbers are things that make *no* sense without context or domain knowledge, such as `1440` or `2.828`. Just use `-1` with the comment `// This is the sentinel value`, problem solved. If you *really* don't want any magic numbers, put them in `magic_numbers.h` with a comment for every one. – paxdiablo Apr 05 '20 at 02:08
  • @rici: “An implementation may accept other forms of constant expressions” was in C 1990. – Eric Postpischil Apr 05 '20 at 03:25
  • @MarcoBonelli: I do not know what that document is, but it is dated 1988, so it is not the 1989/1990 C standard. The penultimate sentence of ISO/IEC 9899:1990 (E) 6.4 is “ An implementation may accept other forms of constant expressions.” – Eric Postpischil Apr 05 '20 at 03:29
  • @EricPostpischil oh, you're right, sorry about that. I was looking at the wrong link and that is indeed C89, not C90. So apparently the change is between C89 and C90. My bad. – Marco Bonelli Apr 05 '20 at 03:37

1 Answers1

6

static const int sentinel = -1; static int n = sentinel; is conforming C code. It is not strictly conforming C code.

C 2018 defines a strictly conforming program to be one that “shall use only those features of the language and library specified in this document” (C 2018 4. 5). Strictly conforming programs are those that only use the core language that is fully defined in the standard. It defines a conforming program to be one that is “that is acceptable to a conforming implementation” (4. 7). For hosted implementations, a conforming implementation, is one that accepts any strictly conforming program (4. 6)—that is, any compiler or other implementation that supports the core C language but that may also have extensions.

6.7.9 4 says “All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.” sentinel is clearly not a string literal. Is it a constant expression? Constant expressions are defined in 6.6. With one exception, they must have operands that are integer constants (that is, literals such as 37), sizeof expressions that yield integer constants, _Alignof expressions, floating-point constants, enumeration constants, character constants, or unary & expressions with certain constraints. sentinel is none of these.

The exception is that paragraph 10 says “An implementation may accept other forms of constant expressions.” So, GCC and other compilers are free to accept this code if they wish, and therefore, since it is accepted by a conforming implementation, it is conforming code. However, since it is implementation-defined whether this is accepted or not, it is not strictly conforming code.

This is substantially similar in prior C standards back to 1990, although there are minor changes, such as that _Alignof was not in early versions of the standard.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • *The exception is that paragraph 10 says “An implementation may accept other forms of constant expressions.”* - Since this does not seem to be the case for C89 though (at least for the version I was able to find), this means GCC/clang or whichever compiler accepts `static int n = sentinel` is non-conforming, doesn't it? I tried that on Godbolt adding `-std=c89` and no error or warning popped up. Or maybe this means `-std=c89` == C90? – Marco Bonelli Apr 05 '20 at 03:40
  • Getting back to the OP, this is not a constraint violation, so no diagnostic is required. Gcc *could* issue a warning, but apparently it doesn't. – rici Apr 05 '20 at 05:11
  • *since it is accepted by a conforming implementation, it is conforming code.* is a very flexible reading of *It defines a conforming program to be one that is “that is acceptable to a conforming implementation”*. Using this logic, a program can use any extension provided by a *conforming* compiler, such as inline assembly and intrinsics and still be conforming. – chqrlie Apr 05 '20 at 10:50
  • 1
    @chqrlieforyellowblockquotes: Programs containing GCC inline assembly are conforming. The C standard is intended to allow extensions. It invites them. Only the *strictly conforming* core language is rigid. The language of *conforming programs* is flexible. Keep in mind when the C standard was started, there was a great variety in platforms where C was implemented. C provided a way to write code that was largely portable not just in today’s sense that it could be used without change but in the sense that it provided a common core so programs could be easily adapted to new implementations. – Eric Postpischil Apr 05 '20 at 11:29