Is it safe to assume that the NULL constant is zero?

Is it safe to assume that the NULL constant is zero?

NULL will compare equal to 0.
NULL is very commonly a zero bit pattern. It is possible for NULL to be a non-zero bit pattern - but not seen these days.


OP is mixing as least 4 things: NULL, null pointer constant, null pointer, comparing a null pointer to 0. C does not define a NULL constant.

NULL

NULL is a macro "which expands to an implementation-defined null pointer constant" C17dr § 7.19 3

null pointer constant

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. C17dr § § 6.3.2.3 3

Thus the type of a null pointer constant may be int, unsigned, long, ... or void * .

When an integer constant expression1, the null pointer constant value is 0. As a pointer like ((void *)0), its value/encoding is not specified. It ubiquitously does have the bit pattern of zeros, but is not specified so.

There may be many null pointer constants. They all compare equal to each other.

Note: the size of a null pointer constant, when it is an integer, may differ from the size of an object pointer. This size difference is often avoided by appending a L or two suffix as needed.

null pointer

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. C17dr § § 6.3.2.3 3

Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal. C17dr § § 6.3.2.3 4

The type of null pointer is some pointer, either an object pointer like int *, char * or function pointer like int (*)(int, int) or void *.

The value of a null pointer is not specified. It ubiquitously does have the bit pattern of zeros, but is not specified so.

All null pointer compare as equal, regardless of their encoding.

comparing a null pointer to 0

if(!ptr) is the same as if(!(ptr != 0)). When the pointer ptr, which is a null pointer, is compared to 0, the zero is converted to a pointer, a null pointer of the same type: int *. These 2 null pointers, which could have different bit patterns, compare as equal.


So when it is not safe to assume that the NULL constant is zero?

NULL may be a ((void*)0) and its bit pattern may differ from zeros. It does compare equal to 0 as above regardless of its encoding. Recall pointer compares have been discussed, not integer compares. Converting NULL to an integer may not result in an integer value of 0 even if ((void*)0) was all zero bits.

printf("%ju\n", (uintmax_t)(uintptr_t)NULL); // Possible not 0

Notice this is converting a pointer to an integer, not the case of if(!ptr) where a 0 was converted to a pointer.

The C spec embraces many old ways of doing things and is open to novel new ones. I have never came across an implementation where NULL was not an all zeros bit pattern. Given much code exist that assumes NULL is all zero bits, I suspect only old obscure implementations ever used a non-zero bit-pattern NULL and that NULL can be all but certain to be an all zero bit pattern.


1 The null pointer constant is 1) an integer or 2) a void*. "When an integer ..." refers to the first case, not a cast or conversion of the second case as in (int)((void*)0).


if(!ptr) is a safe way to check for a NULL pointer.

The expression !x is exactly equivalent to 0 == x. The constant 0 is a NULL pointer constant, and any pointer may be compared for equality against a NULL pointer constant.

This holds true even if the representation of a null pointer is not "all bits 0".

Section 6.5.3.3p5 of the C standard regarding the ! operator states:

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).

And section 6.3.2.3p3 regarding pointer conversions states:

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. 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.


chux has written a good, detailed answer, but regarding that book specifically, I'd be sceptic about its quality:

  • This constant may or may not be a constant zero

    This is wrong, it must always be a zero or a zero cast to a void*. The definition of a null pointer constant is found in C17 6.3.2.3/3:

    An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. 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.

    This means that all integer constant expressions like 0, 0L, 0u, 0x0, '\0' etc are null pointer constant. If any of them is cast to a void*, it is also a null pointer constant.

  • A C programmer need not be concerned with their actual internal representation.

    The author is obviously mixing up the two formal terms null pointer constant and null pointer. A programmer do not need to concern themselves with the internal representation of a null pointer. They do need to know what makes a valid null pointer constant though. The safest, most readable way being to use the NULL macro, which is guaranteed to be a null pointer constant.

So regarding your question "is it safe for me to do things like the below in my code" - yes it is perfectly safe to do !ptr to check for a null pointer, even though ptr==NULL is more readable code.