How to write C/C++ code correctly when null pointer is not all bits zero
According to the C spec:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. 55) If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
So 0
is a null pointer constant. And if we convert it to a pointer type we will get a null pointer that might be non-all-bits-zero for some architectures. Next let's see what the spec says about comparing pointers and a null pointer constant:
If one operand is a pointer and the other is a null pointer constant, the null pointer constant is converted to the type of the pointer.
Let's consider (p == 0)
: first 0
is converted to a null pointer, and then p
is compared with a null pointer constant whose actual bit values are architecture-dependent.
Next, see what the spec says about the negation operator:
The result of the logical negation operator ! is 0 if the value of its operand compares unequal to 0, 1 if the value of its operand compares equal to 0. The result has type int. The expression !E is equivalent to (0==E).
This means that (!p)
is equivalent to (p == 0)
which is, according to the spec, testing p
against the machine-defined null pointer constant.
Thus, you may safely write if (!p)
even on architectures where the null pointer constant is not all-bits-zero.
As for C++, a null pointer constant is defined as:
A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type.
Which is close to what we have for C, plus the nullptr
syntax sugar. The behavior of operator ==
is defined by:
In addition, pointers to members can be compared, or a pointer to member and a null pointer constant. Pointer to member conversions (4.11) and qualification conversions (4.4) are performed to bring them to a common type. If one operand is a null pointer constant, the common type is the type of the other operand. Otherwise, the common type is a pointer to member type similar (4.4) to the type of one of the operands, with a cv-qualification signature (4.4) that is the union of the cv-qualification signatures of the operand types. [ Note: this implies that any pointer to member can be compared to a null pointer constant. — end note ]
That leads to conversion of 0
to a pointer type (as for C). For the negation operator:
The operand of the logical negation operator ! is contextually converted to bool (Clause 4); its value is true if the converted operand is true and false otherwise. The type of the result is bool.
That means that result of !p
depends on how conversion from pointer to bool
is performed. The standard says:
A zero value, null pointer value, or null member pointer value is converted to false;
So if (p==NULL)
and if (!p)
does the same things in C++ too.
It doesn't matter if null pointer is all-bits zero or not in the actual machine. Assuming p
is a pointer:
if (!p)
is always a legal way to test if p
is a null pointer, and it's always equivalent to:
if (p == NULL)
You may be interested in another C-FAQ article: This is strange. NULL is guaranteed to be 0, but the null pointer is not?
Above is true for both C and C++. Note that in C++(11), it's preferred to use nullptr
for null pointer literal.
This answer applies to C.
Don't mix up NULL
with null pointers. NULL
is just a macro guaranteed to be a null pointer constant. A null pointer constant is guaranteed to be either 0
or (void*)0
.
From C11 6.3.2.3:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant 66). If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
66) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.19.
7.19:
The macros are
NULL
which expands to an implementation-defined null pointer constant;
Implementation-defined in the case of NULL
, is either 0
or (void*)0
. NULL
cannot be anything else.
However, when a null pointer constant is assigned to a pointer, you get a null pointer, which may not have the value zero, even though it compares equal to a null pointer constant. The code if (!p)
has nothing to do with the NULL
macro, you are comparing a null pointer against the arithmetic value zero.
So in theory, code like int* p = NULL
may result in a null pointer p
which is different from zero.
Back in the day, STRATUS computers had null pointers as 1 in all languages.
This caused issues for C, so their C compiler would allow pointer comparison of 0 and 1 to return true
This would allow:
void * ptr=some_func();
if (!ptr)
{
return;
}
To return
on a null ptr even though you could see that ptr
had a value of 1 in the debugger
if ((void *)0 == (void *)1)
{
printf("Welcome to STRATUS\n");
}
Would in fact print "Welcome to STRATUS"