11

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.

Dan Sheppard
  • 248
  • 3
  • 10
  • 1
    I can't help thinking this /must/ be a duplicate, but can't find any record of such a question despite extensive clicking around. – Dan Sheppard Feb 25 '15 at 11:24
  • 4
    Related: [What is the difference between NULL, '\0' and 0](http://stackoverflow.com/q/1296843/96780) (Tagged C++, C) – Daniel Daranas Feb 25 '15 at 11:26
  • 4
    Related: [Is NULL always zero in C?](http://stackoverflow.com/q/9894013/96780) (Tagged C) – Daniel Daranas Feb 25 '15 at 11:26
  • 1
    Related: [Is ((void*)0) a null pointer constant?](http://stackoverflow.com/q/26477209/96780) (Tagged C) – Daniel Daranas Feb 25 '15 at 11:27
  • Related: [Why are NULL pointers defined differently in C and C++?](http://stackoverflow.com/q/7016861/96780) (Taggged C++, C) – Daniel Daranas Feb 25 '15 at 11:28
  • "There is already a similar question on this site about C++" -> I suggest you link it – Antonio Feb 25 '15 at 11:29
  • I'm lazy; I use `0`: `srand(time(0));` – pmg Feb 25 '15 at 11:35
  • I have no idea how to do that Antonio (is this markdown or what?), but I will try to find out. – Dan Sheppard Feb 25 '15 at 11:39
  • 1
    Agreed @alk . Thanks for finding the duplicate. I did try really quite hard to find the duplicate, but my search-fu must be poor today! Should I delete this Q? – Dan Sheppard Feb 25 '15 at 11:42
  • Deleting a question with answers isn't nice with respect to those answers, so just leave it as is. And btw: it was *Daniel Daranas* who found the question here: http://stackoverflow.com/questions/28717771/null-or-0-in-c-hard-restrictions?noredirect=1#comment45722648_28717771 – alk Feb 25 '15 at 11:43
  • Indeed, to 20 answers in total, but you used your editorial and comprehension skills to find which of the links he posted contained the answer to my quesiton. That led me to examine that quesiton more closely and discover that the third answer was what I was after. – Dan Sheppard Feb 25 '15 at 11:49
  • Having tried to apply this, actually I don't think it does answer my question! I'll clarify the question, though. – Dan Sheppard Feb 25 '15 at 12:04
  • Ok, I updated the question, Daniel Datanas, @alp. Sorry, that was completely my fault: due to a dodgy sentence, in effect I asked a question other than the one I wanted an answer to. I've read so much about this today! – Dan Sheppard Feb 25 '15 at 12:17
  • I just posted links to four related questions because they were all well formulated and could be useful to the readers of this one. I wasn't specifically aiming to find a duplicate of this one, which looked a too vague to me. Now the question is marked as duplicate, so further editing it isn't likely to help you. – Daniel Daranas Feb 25 '15 at 13:04
  • Hi Daniel, I didn't mean it as a criticism! Thanks for posting the links. – Dan Sheppard Feb 25 '15 at 13:25
  • Especially after the edit, I think this isn't a duplicate question. I also edited the title and voted to reopen. – nwellnhof Feb 25 '15 at 13:34
  • 1
    See also this on meta. From the discussion there, I'm starting to think that my edit was too substantial to make to a question. What a mess, :-( . Sorry. http://meta.stackoverflow.com/questions/286877/my-answer-is-that-i-was-asking-the-wrong-question-question-editing-duplicat/286879#286879 – Dan Sheppard Feb 25 '15 at 13:38
  • @DanSheppard That is correct. You shouldn't just change one existing question to another just because you "really" wanted to ask something else. It doesn't matter if it was you who asked the original question. If you first asked A and then you realised you wanted to ask B, you should create a new question asking B. – Daniel Daranas Feb 25 '15 at 14:04
  • At the time I considered it a clarification (that I was interested in all cases of the use of 0, not just in conditionals and direct assignment). I now think that wasn't so much a clarification as a different question. It's not so much changing "A" to "B" as "A" to "a" or maybe "ä". To be honest, I wish I never wrote it at all. But what should I do now? Change it back again? Can someone with mod privileges maybe reverse my edit? – Dan Sheppard Feb 25 '15 at 14:26
  • see this post: http://stackoverflow.com/questions/1296843/what-is-the-difference-between-null-0-and-0 the rules are the same in C++ and C(approximately), because C is a subset of C++(approximately) – alabamajack Feb 25 '15 at 11:29
  • @DanSheppard I think you should be able to roll back your edit yourself ... in the edit history, does the previous revision have a "rollback" command next to it? – Ajean Feb 25 '15 at 15:59
  • How do I see the edit history @Ajean ? If I click on edit I get a dropdown with revisions in, but I'm not sure that's what you mean, and there's no rollback mentioned. – Dan Sheppard Feb 25 '15 at 16:08
  • Yes, that's what I mean. In each revision heading there should be clickable text such as "source" "link". Is "rollback" there? (It's not for me but I wouldn't be able to do it). If it's not, it must be because the question's already closed. – Ajean Feb 25 '15 at 16:12
  • No, I can't see anything like that @Ajean . I guess you're right, it's because it's closed. Oh well, we'll see wjat happens! I hope someone who's privileged comes to sort it out. – Dan Sheppard Feb 25 '15 at 16:25

3 Answers3

7

Make the intentions of your code clear.

NULL for a pointer.

0, 0u, 0l, etc. for an integer.

0.0 or 0. or .0 for a double.

0.0f or 0.f or .0f for a float.

'\0' for a nul character.

In Objective-C, nil for a nil object.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • 1
    `0.` for a floating point value will do. There is no need for any nimber of trailing zeros. – alk Feb 25 '15 at 11:33
  • 1
    Thank you for your answer, but I think in my question I mentioned that I was aware of the issues of intent and that this question does not concern those issues. – Dan Sheppard Feb 25 '15 at 11:36
2

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.

dbush
  • 205,898
  • 23
  • 218
  • 273
0

The C standard specifically allows implementations to do

#define NULL 0

(http://port70.net/~nsz/c/c11/n1570.html#7.19p3 : "The macros are NULL, which expands to an implementation-defined null pointer constant"; 0 is a null pointer constant.)

What this means in language-lawyer-ese is that search-and-replace changing every instance of the identifier NULL to the integer constant 0 cannot change the meaning of any strictly conforming program.

However, it is very difficult to be sure that a program is strictly conforming. Other answers have given examples of cases where replacing NULL with 0 can, in fact, change the meaning of a program. I would like to point out the converse: if a program has a bug due to 0 being interpreted as an integer constant rather than a null pointer constant, replacing 0 with NULL does not fix the bug.

A straightforward example of this is any use of execlp:

execlp("ls", "ls", "-l", 0);  // UB: last argument is passed as (int)0
                              // rather than (char *)0 as expected by callee

execlp("ls", "ls", "-l", NULL);         // still buggy, NULL could expand to 0

execlp("ls", "ls", "-l", (char *)0);    // correct
execlp("ls", "ls", "-l", (char *)NULL); // correct but asking for someone to
                                        // "simplify" the code by removing the cast

This type of problem becomes much more common when you're working on old code where not every function is declared with a prototype.

zwol
  • 135,547
  • 38
  • 252
  • 361