How to write C/C++ code correctly when null pointer is not all bits zero

C++CComputer Architecture

C++ Problem Overview


As the comp.lang.c FAQ says, there are architectures where the null pointer is not all bits zero. So the question is what actually checks the following construction:

void* p = get_some_pointer();
if (!p)
    return;

Am I comparing p with machine dependent null pointer or I'm comparing p with arithmetic zero?

Should I write

void* p = get_some_pointer();
if (NULL == p)
    return;

instead to be ready for such architectures or is it just my paranoia?

C++ Solutions


Solution 1 - C++

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.

Solution 2 - C++

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.

Solution 3 - C++

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.

Solution 4 - C++

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"

Solution 5 - C++

If your compiler is any good there are two things (and only two things) to watch out for.

1: Static default initialized (that is, not assigned) pointers won't have NULL in them.

2: memset() on a struct or array or by extension calloc() won't set pointers to NULL.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionivaigultView Question on Stackoverflow
Solution 1 - C++ivaigultView Answer on Stackoverflow
Solution 2 - C++Yu HaoView Answer on Stackoverflow
Solution 3 - C++LundinView Answer on Stackoverflow
Solution 4 - C++Glenn TeitelbaumView Answer on Stackoverflow
Solution 5 - C++JoshuaView Answer on Stackoverflow