Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are NULL and 0 completely equivalent in C?

Are there "hard" reasons for using NULL in preference to 0 in C89/C99+, and interchanging them without a second thought as to deep concerns relating to standards compliance, even in code using very obscure aspects of C?

I am concerned with "hard" things like standards compliance, portability, undefined behaviour, unexpectedly different interactions with unanticipated corners of the language, whether the (hypothetical) Milliard Gargantubrain Segmented Memory Supercomputer would likely release its magic smoke, etc.

There is already a similar question on this site about C++, but I am not concerned about C++. I believe this is one of the areas where C++ behaviour may well differ from C.

In reality, issues of style and intent are important. But don't think that is useful to discuss in a Q&A forum, so they are not on-topic for this question.

The answers which I and others have found refer primarily to the use of NULL vs 0 in testing and assignment (a common case and one where it is safe to use them interchangeably). I'm asking if there is any thinking required at all to take arbitrarily weird but standards-compliant code which you have found, and syntactically substitute NULL for 0 (or possibly vice versa when the 0 is a pointer), ignoring whether that is a wise thing to do stylistically. It is hard to give examples of unanticipated interactions but, for example, function pointers often catch us out, maybe sizeof, ....

I've read the relevant standards sections which describe how 0 and NULL behave. Could someone help me with the impact of the way they are defined in the standard on interchanging them in obscure cases? Or else assure me that there are none.

like image 394
Dan Sheppard Avatar asked Feb 25 '15 11:02

Dan Sheppard


1 Answers

Using the constant 0 (or any constant expression evaluating to 0) anyplace where NULL would be used is safe, as 0 is a null pointer constant.

This is defined in section 6.3.2.3p3 of the C99 (and C11) standards state:

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, when converted to a pointer type, is guaranteed to be converted to a null pointer regardless of how that particular system implements a null pointer.

Footnote 55 (66 in C11) from the above quote states:

The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant.

Using my CentOS7 system as an example, it defines NULL as:

#define NULL ((void *)0)

While using 0 in place of NULL is safe, it is not completely equivalent, for example if it is used in a context doesn't necessarily expect a pointer. As an example, sizeof(0) would evaluate to the size of an int, while sizeof(NULL) would (on my system at least) evaluate to the size of a void *.

The reverse, using NULL in place of 0, is not the same. If you had this:

int x[5] = { 3, 4, 5, 6, 7 };
int *p = x + 0;

And replaced it with:

int x[5] = { 3, 4, 5, 6, 7 };
int *p = x + NULL;

The code would fail to compile since two pointers cannot be operands of the + operator.

Another example:

int x[5] = { 3, 4, 5, 6, 7 };
void *p = (void *)x - 0;
printf("*p = %d\n", (int *)p);

While not strictly conforming to the standard, some implementations (like gcc) allow pointer arithmetic on void * types and would result in 3 being printed. If you changed out 0 for NULL:

int x[5] = { 3, 4, 5, 6, 7 };
void *p = (void *)x - NULL;
printf("*p = %d\n", (int *)p);

If you ran this code on a system that 1) allowed void * arithmetic and 2) used a non-zero address for a null pointer, you would be creating and dereferencing a pointer pointing before the start of the array and possibly not correctly aligned.

like image 180
dbush Avatar answered Sep 27 '22 19:09

dbush