1

I am quite surprised that C does not guarantee that certain (constant) expressions are evaluated at compile (translation) time.

C11 (6.6 Constant expressions) (emphasis added):

A constant expression can be evaluated during translation rather than runtime, ...

Hence, two questions:

  1. Is there a standard way to guarantee that a certain (constant) expressions will be evaluated at compile (translation) time?
  2. Extra question: why C does not guarantee it? What were the (technical) obstacles to guarantee it?
pmor
  • 1,684
  • 2
  • 13
  • 18
  • 4
    You can "guarantee" it by typing in the evaluated version. What's the problem you're trying to solve? Normally this is done in an optimized build. but if you've hit a circumstance where it doesn't, that would be worth explaining in more detail. – tadman Apr 26 '21 at 10:58
  • 1
    I think it's safe to assume that a C compiler will evaluate whatever constant expressions it can at compile time ([within reason](https://stackoverflow.com/q/3136686/1679849)). If it really matters, you could use a Makefile to compile and run a program that does all these evaluations and outputs a header file that you can then include in the rest of your C source code. – r3mainer Apr 26 '21 at 11:11
  • 2
    5.1.2.3/1 "The semantic descriptions in this International Standard describe the behavior of an abstract machine in which issues of optimization are irrelevant." – Ian Abbott Apr 26 '21 at 11:16
  • 3
    Not in theory. A C interpreter that reports all required diagnostic messages in an initial scan of a program and defers all other work until execution is, in regard to the observable behavior required by the C standard, indistinguishable from a traditional C implementation with separate compiling, linking, and execution. – Eric Postpischil Apr 26 '21 at 11:32
  • 2
    Enum members are calculated at compile time. E.g. `enum {A = 1, B = A + 7, C = A + B, D = C / B};`. Please don't ask me how far this can be stretched. – pqans Apr 26 '21 at 11:54
  • Just for 'entertainment' value, here is Microsoft's take on [C Constant Expressions](https://docs.microsoft.com/en-us/cpp/c-language/c-constant-expressions?view=msvc-160). – Adrian Mole Apr 26 '21 at 14:42
  • 1
    Is there a specific case you're seeing where this is affecting you? If so, could you show that code? – dbush Apr 26 '21 at 14:49

2 Answers2

1

This may be just mildly ambiguous use of English: The term, "can," is (IMHO) here used to express the logical possibility that the expression is compile-time evaluable, rather than an optional, implementation directive.

If we take the full text of the paragraph you cite from the Standard (bold italics for emphasis are mine), then there is, I believe, a guarantee that the evaluation effectively takes place at compile time:

6.6 Constant expressions

Description
2   A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.

Adrian Mole
  • 30,672
  • 69
  • 32
  • 52
0

The C standard in general is unhelpful in describing what may go on at compile-time and run-time respectively. The whole language is based on the (rather unhelpful) concept of an "abstract machine" in C17 5.1.2.3

The semantic descriptions in this International Standard describe the behavior of an abstract machine in which issues of optimization are irrelevant.
...
Evaluation of an expression in general includes both value computations and initiation of side effects...
In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced...

The above includes the formal definition of evaluation, which is also rather unhelpful. Basically the implementation is free to do anything in the ways of optimizations as long as side effects and "the observable behavior" are respected. There are no requirements placed stating that the compiler must perform certain optimizations. It's possible to build a compiler with no optimizations at all and it can still be a conforming implementation.

As for 6.6 and constant expression, the intention is surely to state the rules for what makes valid compile-time constants. In case integer constant expressions aren't evaluated at compile-time, then it becomes very impractical to generate an executable, to the point where the whole language would collapse. Indirectly, there are many requirements that can't be fulfilled unless constant expressions are evaluated at compile-time.

For example, if array sizes of static storage duration objects weren't known at compile-time, then how would you generate the start-up code for initializing them? It must occur before main() is called - this is actually guaranteed by 5.1.2:

All objects with static storage duration shall be initialized (set to their initial values) before program startup.

And if main contains static int arr [5] = {1,2,3,4,5}; then this object must already be initialized when we reach the declaration. So we can only fulfil 5.1.2 if the integer constant 5 in my example, an integer constant expression, is in fact evaluated at compile-time.

Lundin
  • 155,020
  • 33
  • 213
  • 341
  • `before program startup.` But "program startup" starts when `main` is called. If the code would be ex. `const int var = {1+2};` then a compiler can generate code `void __my_super_init() { var = 1 + 2; }` to be executed before `main` and _at runtime_ calculate the result. Right? That something is initialized, does not mean it's calculated at compile-time. – KamilCuk Apr 26 '21 at 12:31
  • @KamilCuk On embedded systems with true NVM, all initializers are stored in flash memory and copied down into static storage duration objects before main() is called. This happens in run-time, but before main(). That's why, I suppose, such start-up code is often referred to as the "C run-time" - it enables run-time execution of standard C, but that doesn't mean that other things that aren't necessarily standard C can execute before main(), but also in run-time. – Lundin Apr 26 '21 at 12:38