The nullptr proposal leaves its type open
N2394 proposes a null pointer constant for C while allowing a minimal implementation based on void pointers.
A null pointer constant can express the absence of a pointer while still having an integer type. That is the source of several problems in C's existing NULL macro. Jens Gustedt's N2394 proposal, dated June 9 and listed in the WG14 log, proposes a new spelling with more restricted uses. Its most interesting compromise is that it does not require every implementation to add a distinct null-pointer type.
C's small language model appeals to me, but a small model can leave one expression serving several purposes. Zero is useful as an integer constant and as the source of a null pointer conversion. Those uses become difficult to distinguish when a macro hides the expression and the surrounding context cannot supply the intended pointer type.
NULL has several possible types
The existing rule in the C11 draft N1570 defines a null pointer constant as an integer constant expression with value zero, or such an expression cast to void pointer. The library's NULL macro expands to an implementation-defined null pointer constant. A spelling that is stable in source can therefore expand to expressions with different types.
A function parameter with a declared pointer type often supplies the missing context. Passing zero to that parameter triggers the null pointer conversion required by the language. The receiver obtains a null pointer of its parameter type. That familiar case can conceal the fact that the original expression was an integer.
An argument corresponding to an ellipsis has no such parameter type. Default argument promotions apply, and the callee uses its own convention to retrieve the argument. An integer zero does not acquire a pointer type merely because a variadic function expects one. The caller and callee need an agreement that the syntax of the ellipsis does not express.
A current C interface can make that agreement explicit by supplying the intended pointer type. This example is only a declaration and a call site for an imagined sentinel-based interface:
void names(const char *first, ...);
void supply_names(void)
{
names("alpha", "beta", (char *)0);
}
The final expression has pointer type regardless of how NULL is defined. The callee must still follow the corresponding argument convention. A typed sentinel addresses this particular call-site ambiguity, while the broader design of a variadic interface remains the application's responsibility.
Generic selection can observe the macro
C's generic selection makes type differences observable without an ellipsis. A macro can select different expressions according to the type of its controlling expression. Replacing an integer zero with a void pointer can consequently change which association is selected, even when both expressions are null pointer constants.
The proposal also discusses conditional expressions. An expression combining an integer value and NULL can be an ordinary arithmetic expression on one implementation and a constraint violation on another. A reader cannot settle its validity from the macro's name. The expansion determines which language rules apply.
That is a stronger reason for a dedicated spelling than cosmetic consistency. The intention to provide a null pointer should constrain how an expression can participate in arithmetic. A name whose expansion may be an integer cannot independently enforce that intention. The surrounding syntax has to supply the missing distinction.
I prefer a null spelling that makes misuse easier to diagnose. C interfaces already require close attention to conversions, and a null constant should not silently become an ordinary integer operand just because one header chooses a particular definition.
The proposal permits a minimal implementation
N2394 offers two implementation approaches. A compiler with an existing C++ frontend can reuse machinery for a first-class null constant. An implementation without that machinery can provide a predefined macro equivalent to the following:
#define nullptr ((void *)0)
This is an illustration of the proposal's implementation strategy, not a claim that existing C standards reserve this spelling or provide the feature. The paper attempts to keep the implementation cost small enough for C compilers that cannot simply share a C++ frontend.
Its proposed type is unspecified, subject to the requirement that it is not arithmetic. That choice is consequential for generic selection. A program cannot assume a universal distinct type and use that assumption to select a portable generic association. The proposed wording explicitly warns that type-dependent generic selection can give nonportable results.
The paper encourages a stronger implementation with a type incompatible with other types and restrictions on forming objects. That recommendation is different from a requirement imposed on every compiler. Describing this revision as a mandatory C++-style type would erase the compromise that the proposal makes.
Stronger intent still has diagnostic limits
A first-class compiler construct can retain information that a macro expansion loses. Once a preprocessor has replaced a spelling with a void pointer expression, some later operations look like ordinary operations on that type. The proposal recognizes that this limits which prohibited uses an implementation can diagnose as constraints.
The explanatory text calls out conversion to non-pointer types and conditional expressions mixing the new constant with integer zero. The proposed wording restricts usage and recommends diagnostics, while allowing the minimal implementation. That combination gives implementations room to differ in diagnostic strength.
For arguments subject to default promotions, the proposal specifies replacement with a null void pointer. This removes the specific possibility of passing an integer merely because NULL happens to expand to integer zero. It does not make the ellipsis describe arbitrary expected pointer types. Interface authors still have to state what a variadic function retrieves and which conversions its callers must supply.
That limit matters for interoperation, where C declarations often provide the shared contract. A new null constant can reduce one source of ambiguity without changing the fundamental absence of parameter typing after an ellipsis. Its effect must be explained at the conversion point.
Replacing the macro is part of the design
N2394 proposes deprecating NULL and recommends using the new spelling in its expansion. It also calls for replacing references throughout the standard. These are proposed migration provisions, not changes that an application can assume its June compiler or library already implements.
The migration direction gives existing source a possible route to stronger diagnostics through the familiar macro. It also raises the importance of code that has treated NULL as an integer. Such code depends on an expansion choice that was never a useful statement of pointer intent. Restricting that use can expose assumptions that were previously hidden by a header.
The proposal's practical question is how much diagnostic information C should require from a null expression while preserving an inexpensive implementation. Its unspecified type leaves generic code with fewer guarantees than a universally distinct type would provide. That is the tradeoff in this revision, and it determines which claims a library author can safely make about the proposed spelling.