Doesn't the bolded sentence contradict the author's claim that ((void*)0)
is not a null pointer constant?
No, it doesn't. (I confess to being a bit biased, since the referenced blog is mine.)
The bolded sentence says that its type and value are identical to those of the unparenthesized expression. That's not enough to imply that it's a null pointer constant.
Consider:
void *var = 0;
(void*)0
is a null pointer constant. ((void*)0)
has the same type and value as (void*)0
. var
also has the same type and value as (void*)0
, but var
clearly is not a null pointer constant.
Having said that, I'm 99+% sure that the intent is that ((void*)0)
is a null pointer constant, and more generally that any parenthesized null pointer constant is a null pointer constant. The authors of the standard merely neglected to say so. And since the description of parenthesized expressions in 6.5.1p5 specifically enumerates several other characteristics that are inherited by parenthesized expressions:
A parenthesized expression is a primary expression. Its type and value
are identical to those of the unparenthesized expression. It is an
lvalue, a function designator, or a void expression if the
unparenthesized expression is, respectively, an lvalue, a function
designator, or a void expression.
the omission is troubling (but only mildly so).
But let's assume, for the sake of argument, that ((void*)0)
is not a null pointer constant. What difference does it make?
(void*)0
is a null pointer constant, whose value is a null pointer of type void*
, so by the semantics of parenthesized expressions ((void*)0)
also has a value that is a null pointer of type void*
. Both (void*)0
and ((void*)0)
are address constants. (Well, I think they are.) So what contexts require a null pointer constant and do not accept an address constant? There are only a few.
6.5.9 Equality operators
An expression of function pointer type may be compared for equality to a null pointer constant. (An object pointer may be compared to an expression of type void*
, but a function pointer may not, unless it's a null pointer constant.) So this:
void func(void);
if (func == ((void*)0)) { /* ... */ }
would be a constraint violation.
6.5.16.1 Simple assignment
In an assignment, a null pointer constant may be assigned to an object of pointer-to-function type, and will be implicitly converted. An expression of type void*
that's not a null pointer constant may not be assigned to a function pointer. The same constraints apply to argument passing and initialization. So this:
void (*fp)(void) = ((void*)0);
would be a constraint violation if ((void*)0)
were not a null pointer constant. Thanks to commenter hvd for finding this.
7.19 Common definitions <stddef.h>
The macro NULL
expands to "an implementation-defined null pointer constant". If ((void*)0)
is not a null pointer constant, then this:
#define NULL ((void*)0)
would be invalid. This would be a restriction imposed on the implementation, not on programmers. Note that this:
#define NULL (void*)0
is definitely invalid, since macro definitions in standard headers must be fully protected by parentheses where necessary (7.1.2p5). Without the parentheses, the valid expression sizeof NULL
would be a syntax error, expanding to sizeof (void*)
followed by an extraneous constant 0
.