2026-09-09
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 |
none
WG21
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 |
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.
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.
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.
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.
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.
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.
based on a version (to be determined) after integration of solution for C23 issue 1020
6 If an object or subobject declared with storage-class specifier
constexprhas pointer, integer, or arithmetic type, any explicit initializer value for it shall benullan 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 signalingNaNvalue, the unqualified versions of the type of the initializer and the corresponding real type of the object declared shall be compatible.
7 NOTE 1
TheA named constant or compound literal constant corresponding to an object declared with storage-class specifierconstexprand pointer type isa constant expression with a value null, and thus a null pointer andan address constant. Thus, such a named constant is a valid initializer for otherconstexprdeclarations, provided the pointer types match accordingly. However, even if it has typevoid*and is null it is not a null pointer constant.
…
10 EXAMPLE 2 Pointers can be initialized to eligible address constants
constant expressions, such asanull pointer constants or addresses of objects or functions with static storage duration:constexpr int *p = {}; // Default initialization with a null pointerextern double getCloser(double); constexpr double (*evenCloser_p(double)) = getCloser; static unsigend long item; constexpr unsigend long* itp = &item;
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) orone of the categories that are described in this clause:
- a compound literal constant,
- a named constant,
- an integer constant expression,
- an arithmetic constant expression, or
- an address constant.
…
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,
oran integer constant cast to pointer type, or implicitly using an expression of array or function type. An address constantforthat 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.
add a new paragraph of constraints
Constraints
…
7′ For a declaration of an identifier
IDwithconstexprwhere the declared type is pointer to typeTand 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 typeSand there shall be qualifiersCVR, possibly empty, such that the typeSqualified withCVRis compatible toT. 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.