Wording for “Constexpr function and object pointer initialisation” rev 1

Jens Gustedt, INRIA and ICube, France

Niall Douglas, ned Productions Ltd., Ireland

2026-09-09

Preamble

contributing

Niall Douglas (rationale, history)

Jens Gustedt, Niall Douglas, Martin Uecker, Joseph Myers, Javier Múgica (wording)

number Title Authors Remarks
N3993 rev 1 Wording Group this paper, 2026-10-12
N3937 rev 0 Douglas, Niall rationale, 2026-07-15
N3953 Draft minutes 2026 (summer) Svoboda, David polled N3937, 2026-08-17
N3886 Working Draft base for diff, 2026-06-26

LaTeX document branch

none

Liaison

WG21

Relevant polls

Straw Poll: Would WG14 like something along the lines of N3937 for address constants for both function and object pointers and to send the paper to the Wording Group?

meeting date for against abstain
August 2026 [N3953] 2026-08-17 17 1 7

Wording choices

We took the mission formulated by WG14 literally, in that the goal is to allow address constants in the initialization of constexpr with pointer type. This makes the specification in 6.2.1 relatively easy, but necessitates some repair work later in places where address constants are used.

Notes and examples

Previously these showed the restricted case of C23, namely that constexpr with pointer type could only be initialized with a null pointer. Since now this would not hold anymore, some minor, non-normative adaptions are in order.

Avoid undefined behavior

Currently there are several cases of possible undefined behavior concerning address constants. The premise since its introduction to C was that this should not be allowed for named constants defined with constexpr.

Out-of-range pointers

This code is allowed in C23:

extern double array[];
static double* ap = &array[3];

Here array might be defined in a different TU than ap, and so the size information, or more precisely the length of the array, is only known in the other TU.

So for the code at hand the expression &array[3] is a valid address constant as long as array happens to have at least four elements at the start of a program execution. If it has less elements, the behavior is undefined. In general this will lead to a crash a program startup, but at the worst could lead to an out-of-bounds access.

Currently the text says

An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, …

A strict interpretation of this wording is, that not only an address constant has to be a valid pointer, but also that dereferencing it must be possible and lead to a valid lvalue. Therefore, we propose only to allow pointers to objects in constexpr initializers, where the size is known in phase 7, and such that possible offsets with integer constants expressions can be verified during phase 7.

Misalignment when using address constants

Address constants that would be converted from their original type to another target type could be misaligned for the target type, also leading to undefined behavior. Thus here again, the validity of the pointer depends on properties that are only known during phase 8 or (for different TU) only at program startup. Avoiding such situations quickly becomes complicated and we didn’t think that working around this would be too beneficial at the moment.

Therefore we only allow initializers for constexpr that have the same base type as the pointer that is to be initialized.

As a consequence, this forbids constexpr void* unless the initializer is a null pointer constant.

Legend for the wording

Deletions in the shown standard text are as shown here, additions as shown here, code snippets as here. These may be rendered differently according to the style in which the document is shown by your browser but should always be well distinguishable. In the style provided there are two visual distinctions:

Close to each other proposed changes resemble like this.

Proposed wording

based on a version (to be determined) after integration of solution for C23 issue 1020

Modifications in clause 6.2.1

6 If an object or subobject declared with storage-class specifier constexpr has pointer, integer, or arithmetic type, any explicit initializer value for it shall be null an address constant, an integer constant expression, or an arithmetic constant expression, respectively. If the object declared has real floating type, the initializer shall have integer or real floating type. If the initializer has decimal floating type, the object declared shall have decimal floating type and the conversion shall preserve the quantum of the initializer. If the initializer has real type and a signaling NaN value, the unqualified versions of the type of the initializer and the corresponding real type of the object declared shall be compatible.

7 NOTE 1 The A named constant or compound literal constant corresponding to an object declared with storage-class specifier constexpr and pointer type is a constant expression with a value null, and thus a null pointer and an address constant. Thus, such a named constant is a valid initializer for other constexpr declarations, provided the pointer types match accordingly. However, even if it has type void* and is null it is not a null pointer constant.

…

10 EXAMPLE 2 Pointers can be initialized to eligible address constants constant expressions, such as a null pointer constants or addresses of objects or functions with static storage duration:

constexpr int *p = {}; // Default initialization with a null pointer
extern double getCloser(double);
constexpr double (*evenCloser_p(double)) = getCloser;
static unsigend long item;
constexpr unsigend long* itp = &item;

Modifications in clause 6.6.1

5 Constant expressions do not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated. Additionally, such a constant expression is, or evaluates to, a null pointer constant (6.3.3.3) or one of the categories that are described in this clause:

…

13 A null pointer constant, a null pointer, a pointer to an lvalue designating an object of static storage duration or a pointer to a function designator is an address constant; it is created explicitly using the unary & operator, a null pointer constant, a named constant of pointer type, a compound literal constant of pointer type, or an integer constant cast to pointer type, or implicitly using an expression of array or function type. An address constant for that is a pointer to an lvalue designating an object of static storage duration with a complete object type plus or minus an integer constant expression is also an address constant.

Addition to clause 6.7.1

add a new paragraph of constraints

Constraints

…

7′ For a declaration of an identifier ID with constexpr where the declared type is pointer to type T and the initializer is an address constant P. P shall be a null pointer constant or shall have a pointer, array or function type with base type S and there shall be qualifiers CVR, possibly empty, such that the type S qualified with CVR is compatible to T. If P is derived from the address of an object definition or compound literal A, then A shall have a complete type and P shall be an address within A.