The book Understanding and Using C Pointers, by Richard Reese says:
The null concept is an abstraction supported by the null pointer constant. This constant may or may not be a constant zero. A C programmer need not be concerned with their actual internal representation.
My question is, since "this constant may or may not be a constant zero," is it safe for me to do things like the below in my code:
int *ptr = NULL;
// Some code which probably sets ptr to a valid memory address
if(!ptr)
{
ERROR();
}
If NULL is not 0, there is a chance that the if clause will evaluate to true.
chux has written a good, detailed answer, but regarding that book specifically, I'd be sceptic about its quality:
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: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 avoid*
, it is also a null pointer constant.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 thoughptr==NULL
is more readable code.if(!ptr)
is a safe way to check for a NULL pointer.The expression
!x
is exactly equivalent to0 == x
. The constant0
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:And section 6.3.2.3p3 regarding pointer conversions states:
NULL
will compare equal to0
.NULL
is very commonly a zero bit pattern. It is possible forNULL
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 pointer constant
Thus the type of a null pointer constant may be
int
,unsigned
,long
, ... orvoid *
.When an integer1, 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
The type of null pointer is some pointer, either an object pointer like
int *, char *
or function pointer likeint (*)(int, int)
orvoid *
.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 asif(!(ptr != 0))
. When the pointerptr
, 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. ConvertingNULL
to an integer may not result in an integer value of 0 even if((void*)0)
was all zero bits.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 assumesNULL
is all zero bits, I suspect only old obscure implementations ever used a non-zero bit-patternNULL
and thatNULL
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)
.