C, Ocean Edition I

6.6 Constant Expressions

Clause
6.6
ISO C99 mapping
6.6
Status
Normative

6.6.1 General

A constant expression is evaluated during translation rather than during execution, and may appear where a value is required at translation time.

constant-expression:
	conditional-expression

Constraints.

A constant expression shall not contain an assignment operator, an increment operator, a decrement operator, a function call, or a comma operator, unless it appears in a subexpression that is not evaluated, such as the discarded branch of a conditional operator or the operand of sizeof.

Each operation in a constant expression shall be one that this specification defines. A constant expression shall not designate an object whose value is not known at translation time.

Semantics.

A constant expression is evaluated using exactly the semantics of clause 6.5. This is not a separate arithmetic:

An implementation that evaluates constant expressions with unbounded precision shall reduce the result to the width and signedness of the expression's type before using it, so that a constant expression and its runtime equivalent produce the same value.

int32_t a = INT32_MAX + 1;            /* INT32_MIN, computed at translation time */
int32_t b = INT32_MAX;
int32_t c = b + 1;                    /* INT32_MIN, computed at execution time */

Traps in constant expressions.

An operation in a constant expression that would cause a defined trap at execution time is a constraint violation, and the implementation shall diagnose it and shall not translate the program.

int a = 1 / 0;             /* diagnosed */
int b = 1 << 64;           /* diagnosed on a 32-bit int */
int c = INT32_MIN / -1;    /* diagnosed */

A trap that is provable but does not occur in a constant expression is still subject to clause 4.3, which requires diagnosis of provable errors wherever they appear.

Difference: ISO C99 leaves the arithmetic of translation-time evaluation partly to the implementation and permits a wider evaluation range. Ocean Edition I requires translation-time and execution-time arithmetic to agree, because a program whose meaning depends on where a value was computed is not a program anyone can reason about. See behavior.md Law 5.

ISO C99 mapping: 6.6, paragraphs 1 through 4.


6.6.2 Integer Constant Expressions

An integer constant expression has integer type and its operands are:

An integer constant expression is required in these positions:

Because there are no variable-length arrays, an array declarator always contains an integer constant expression. There is no second kind of array declarator whose extent is computed at execution time. See clause 6.7.5.2.

ISO C99 mapping: 6.6, paragraph 6.


6.6.3 Arithmetic Constant Expressions

An arithmetic constant expression has arithmetic type and its operands are integer constants, floating constants, enumeration constants, character constants, and sizeof expressions. Casts within it shall convert only to arithmetic types.

Floating constant expressions are evaluated with the translation-time rounding mode, which shall be the default rounding mode of the target unless the program has selected another under clause 7.2.7. The implementation shall document any case where the translation-time result differs from the execution-time result of the same operation.

ISO C99 mapping: 6.6, paragraph 8.


6.6.4 Address Constants

An address constant is a null pointer, a pointer to an object of static storage duration, or a pointer to a function. It is produced by:

static int values[16];

int *first        = &values[0];      /* address constant */
int (*whole)[16]  = &values;         /* address constant */
int (*fn)(int)    = &transform;      /* address constant */

Arithmetic on an address constant with an integer constant expression produces an address constant.

Note. Because arrays do not convert to pointers, an initializer that wants an element pointer writes &values[0]. This form is an address constant, so it remains valid in a static initializer.

ISO C99 mapping: 6.6, paragraph 9.


6.6.5 Initializer Constant Expressions

An object with static storage duration shall be initialized by:

An object with automatic storage duration may be initialized by any expression, evaluated at the point the declaration is reached.

ISO C99 mapping: 6.6, paragraph 7, and 6.7.8.


6.6.6 Constant Folding and Diagnostics

An implementation may evaluate any expression at translation time when it can prove the result. Doing so shall not change the result, shall not remove a trap that would occur, and shall not introduce one that would not.

Where an implementation folds an expression and discovers a provable error, clause 4.3 requires it to diagnose the error rather than to silently produce a value or to discard the computation.

Example.

static const int size = 16;
int buffer[size];

size is a const object with static storage duration and a constant initializer. An implementation shall accept it as an integer constant expression in an array declarator, because its value is known and it cannot change. This is a deliberate extension of the ISO C99 rule, and it is what makes clause 6.7.9 preferable to a macro:

static const int max_users = 64;    /* preferred */
#define MAX_USERS 64                /* not preferred */

See behavior.md section 8 and syntax.md section 52.

Constraint. An object is usable as an integer constant expression when all of the following hold:

A const object that does not satisfy every requirement is not usable as a constant expression, and using it where a constant is required is a constraint violation whose diagnostic shall say which requirement failed.

No self-reference and no forward reference.

The last requirement resolves the cases a reader will ask about. An object is usable only after its own initializer has been completed, so an initializer cannot depend on the object it initializes, and cannot depend on an object declared later.

const int a = 16;
const int b = a * 2;        /* 32; a is complete before b's initializer */

int values[b];              /* array of 32; b is usable here */

const int c = c;            /* invalid: c is not complete within its own initializer */
const int d = e;            /* invalid: e is not yet declared */
const int e = 4;

const int f;                /* invalid as a constant: no explicit initializer */

c is a constraint violation under clause 6.2.1.2, which places an identifier's scope at the completion of its declarator but does not make the object usable as a constant until its initializer is done.

f is worth stating separately. Clause 6.7.9.2 zero-initializes it, so it has a determined value, and it is still not a constant expression. The requirement is an explicit initializer rather than a determined value, so that a reader can see the constant at the declaration. Clause 6.7.9.7 requires a diagnostic for such a declaration on independent grounds.

Volatile. A volatile const object is excluded because clause 6.7.3.4 requires every access to it to actually occur, which is incompatible with reading it at translation time.

Note. The requirements do not mention storage duration, because they do not need to. A block-scope const int with a constant initializer is usable, whether or not it is static, since its value is fixed at translation time either way and clause 4.7.3 makes the object's stability a source contract.

ISO C99 mapping: new. ISO C99 does not admit a const object as an integer constant expression.