August 30, 2024·6 min read·engineeringcc23standardspointers

C23 reaches the final approval stage

ISO records the start of C23 final approval balloting on 30 August. The nullptr proposal shows the compatibility work behind a standard whose implementation is already arriving in compilers and libraries.

A familiar spelling can preserve decades of ambiguity. In C, NULL communicates an intention without specifying one portable type for the expression it expands to. The work to add nullptr illustrates the design effort inside C23 as the revision moves through its remaining approval process. The ISO project record places the start of final approval balloting on 30 August. That is a standards milestone, distinct from the arrival of an implementation in a particular compiler.

C was my first language. Its compact definition remains appealing, but compactness does not make every inherited rule easy to explain. A change that lets the type system express an existing intention is useful when it removes ambiguity without silently changing old programs.

Approval follows earlier technical decisions

ISO's record distinguishes the committee draft, enquiry and approval stages. For this revision, it records approval for FDIS registration in May and receipt or registration of the final text in July. The August entry starts the next approval step. The sequence matters because a final approval ballot is neither the initial proposal of the language changes nor an announcement that every implementation now supports them.

An engineer encounters these stages through several different documents. A proposal explains the problem and a possible wording. A working draft integrates decisions into a complete language definition. An implementation release describes which parts a toolchain actually supports. These documents answer related questions, but substituting one for another can produce a false compatibility claim.

For example, a proposal can contain alternatives or discussion of an earlier design. That material is valuable evidence of motivation, without establishing that every paragraph became normative wording. A release can implement a facility while documenting limits elsewhere. A useful account of the revision needs to preserve the role of each source it cites.

nullptr addresses the type behind the sentinel

Jens Gustedt and JeanHeyd Meneide's N3042 proposal explains the difficulties created by existing null pointer constants. Depending on the implementation, NULL may expand to an integer constant or an expression of pointer type. This matters in contexts where the expression's type participates in selection or argument interpretation. A conventional typed pointer parameter can supply a conversion context that a variadic argument lacks.

The proposal introduces nullptr with a distinct type, exposed as nullptr_t through <stddef.h>. The new constant converts to pointer types and participates in boolean contexts. It does not become an ordinary integer that can be used for arithmetic. Its type makes the absence of a pointer explicit in contexts that inspect types.

This is a concrete improvement in expressiveness. An integer zero can be the value zero in an arithmetic calculation or a null pointer constant in a permitted context. The surrounding language rules decide which interpretation applies. Giving the pointer intention its own constant reduces the amount of context a reader must reconstruct.

The effect is especially visible in generic interfaces. If a macro uses _Generic to select behavior based on type, an integer expression and a pointer expression can select different associations. A portable spelling for the null constant's type gives the interface another case it can identify deliberately. The interface still needs to define which arguments it accepts.

The proposal has to fit the existing calling model

N3042 spends considerable attention on representation and variadic arguments. Its design gives the new type the size and alignment of void *, with the corresponding null representation. It proposes an explicit allowance for interpreting a variadic nullptr_t argument as void * or a pointer to character type. Those details are necessary because an ellipsis does not provide the ordinary typed parameter conversion.

The engineering implication is that source-level intent and calling conventions have to agree. A token that looks like a pointer sentinel does not establish what representation crosses a variadic call. The receiving function must interpret what the caller actually passed according to permitted language rules. The new feature needs to define that agreement, rather than relying on familiar machine behavior.

It would be a mistake to expand this into a claim that nullptr satisfies every variadic pointer contract. A function expecting some other pointer type still needs the correct argument type for that contract. The proposal's explicit allowances are narrower than a universal reinterpretation rule. Preserving that distinction is part of explaining the feature accurately.

I find this compatibility work more interesting than the new keyword itself. A small addition must account for how real C programs pass values across interfaces. Its usefulness depends on making that behavior less ambiguous while respecting the calling model that existing software already uses.

New constants can improve diagnosis

The proposal also discusses replacing NULL with nullptr in existing code. Ordinary pointer initialization can preserve its intention, but type-generic interfaces may need to recognize the new type. Misuse of NULL in an integer context can become a diagnosed problem. That is a source compatibility consequence a migration has to consider.

The difference is useful because a diagnosed mismatch identifies a place where the source's apparent intention needs examination. A programmer who meant an integer zero can say so. A programmer who meant a null pointer can retain the new spelling. Automatic replacement without examining those contexts would skip the design question the new type helps expose.

An API author has a related decision. Accepting a null constant may represent a documented default operation, or it may represent an invalid argument. The language addition cannot supply the domain meaning. A generic interface can distinguish the new type, but its selected behavior still needs to follow the API's contract.

This separates language clarity from application correctness. A pointer value with an explicit null spelling can still be passed to an operation that requires an object. The new type improves how absence is represented. It does not establish that absence is permitted at every call site, and it does not validate the lifetime of a non-null pointer.

Implementation support proceeds on its own schedule

The glibc 2.40 announcement already describes _ISOC23_SOURCE and additional C23 library facilities. It explicitly says that only some C23 facilities are supported. The library's adoption of the standard name is therefore a precise request mechanism, without being a claim of complete conformance.

Similarly, the GCC 14.1 compiler manual accepts -std=c23 while describing support as experimental and incomplete. This is useful evidence available before the current ballot milestone. Implementers can deliver individual facilities while the standard proceeds through formal steps. Project maintainers still need to know which facilities their selected version implements.

The practical compatibility unit is consequently a requirement on a toolchain. A project using a new constant needs a compiler that recognizes its semantics. A project using a new library interface also needs suitable headers and implementation. Treating C23 as a single boolean property of a system hides those independent dependencies.

That distinction also affects public headers. A library can adopt a new facility internally without requiring its consumers to parse it. Using it in an installed header changes what client compilers must accept. The decision belongs in the library's supported-language contract, because every downstream translation unit including the header encounters that requirement.

The proposal also distinguishes nullness from a numeric machine address. A null pointer representation need not be an all-zero bit pattern. Naming the constant directly helps avoid teaching its meaning as a statement about a particular address layout. The language defines the null conversion, and the implementation supplies the appropriate representation.

The milestone gives a date to the process

The final approval stage is useful historical information, but it does not replace the technical questions behind adoption. The nullptr work shows how a small facility can require decisions about expression categories and runtime representation. Those decisions explain what the keyword means more effectively than a list of new spellings.

For a C project, the immediate value is the ability to make a narrow adoption decision. An interface can use a distinct null constant where that expresses its contract more clearly, subject to its supported compilers. Its maintainers can then inspect generic selection and variadic boundaries as specific compatibility concerns. The approval record establishes where the standard stands on this date, while the proposal and implementation documents explain the behavior a project is choosing to require.