NULL vs 0

Started by
43 comments, last by wqking 11 years, 4 months ago
Stroustrup in the C++ FAQ:
"If you have to name the null pointer, call it nullptr; that's what it's called in C++11. Then, "nullptr" will be a keyword."

I think that makes sense. Use nullptr, and if you need pre-C++11 compatibility, define nullptr yourself conditionally like Servant of the Lord suggests, except make the definition as standard-conforming as possible (as shown by Martins Mozeiko) instead of just 0. If you needed both C++ and C compatibility, you'd have to likewise conditionally define nullptr as (void*)0 for C.

NULL is fine in pure C code, but there doesn't seem to be any reason to use it in C++.
Advertisement

[quote name='nife87' timestamp='1355564260' post='5010887']Although NULL IS ugly, it cannot be replaced with 0
What? [font=courier new,courier,monospace]NULL[/font] is [font=courier new,courier,monospace]#define[/font]d as [font=courier new,courier,monospace]0[/font], meaning there's absolutely no difference except style/personal preference, and Bjarne "C++" Stroustrup was quoted earlier as preferring 0 (or nullptr in C++11 where available) over the NULL macro.
[/quote]

Sorry. I was under the impression that NULL was commonly defined 0, but was not required to be defined as such. Well, that certainly makes life a bit easier.
No, you were right. NULL is not required to be 0. In C++, NULL can be defined to be any integral constant expression that evaluates to zero. As I already mentioned, the C++ standard lists two possible conforming definitons: 0 and 0L, though those aren't the only possibilities.
NULL is self documenting, but the documentation is misleading. It gives the impression that the compiler will know you are talking about a pointer, but that's not the case. As others have mentioned, if you have functions foo(int) and foo(int *), foo(NULL) will call the former. This is confusing as hell because NULL indicates one intent and the compiler does something else. IF you use 0, everyone knows what will happen. That seems more clear to me.
NULL because reading code is harder than writing code, and using NULL makes it explicit that the variable being assigned to is a pointer type.


NULL is self documenting, but the documentation is misleading. It gives the impression that the compiler will know you are talking about a pointer, but that's not the case. As others have mentioned, if you have functions foo(int) and foo(int *), foo(NULL) will call the former. This is confusing as hell because NULL indicates one intent and the compiler does something else. IF you use 0, everyone knows what will happen. That seems more clear to me.


That doesn't actually resolve the initial problem you set out - even if you use 0 the wrong overload may still be called (assuming that foo (int *) was the intent). I'd go so far as to argue that using 0 instead of NULL is even more ambiguous in this case - when reading code, if you see a call to "foo (0)" how on earth are you going to know which overload the programmer intended? Using "foo (NULL)" at least gives you a chance - you know the programmer's intent straight away - they want the pointer version (the compiler may still get it wrong but at least you can now detect that and take appropriate remedial action).

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

zero
it's convenient.(Maybe because lazy)
And it's direct,Others see it will soon know,there is nothing.

NULL because reading code is harder than writing code, and using NULL makes it explicit that the variable being assigned to is a pointer type.

[quote name='Álvaro' timestamp='1355574263' post='5010923']
NULL is self documenting, but the documentation is misleading. It gives the impression that the compiler will know you are talking about a pointer, but that's not the case. As others have mentioned, if you have functions foo(int) and foo(int *), foo(NULL) will call the former. This is confusing as hell because NULL indicates one intent and the compiler does something else. IF you use 0, everyone knows what will happen. That seems more clear to me.


That doesn't actually resolve the initial problem you set out - even if you use 0 the wrong overload may still be called (assuming that foo (int *) was the intent). I'd go so far as to argue that using 0 instead of NULL is even more ambiguous in this case - when reading code, if you see a call to "foo (0)" how on earth are you going to know which overload the programmer intended? Using "foo (NULL)" at least gives you a chance - you know the programmer's intent straight away - they want the pointer version (the compiler may still get it wrong but at least you can now detect that and take appropriate remedial action).
[/quote]

That argument is really strange. Nobody expects the foo(0) will call the pointer version. 0 is an int. If the intent is to call the pointer version, you should write a cast, or it won't work, whether you use NULL or 0. Once you use the cast, the intent is clear.

foo(0); // I want to call foo(int)
foo((int *)0); // I want to call foo(int*)

How is that not clear?

The same problem comes up in other situations BTW. As well as the type of literal "0" being ambiguous, there's many similar situations that a C++ programmer should be aware of. e.g. what's the type of the literal "1" below, and does the assertion fail?
unsigned long long mask = 1<<48;
assert( mask == 0x1000000000000 );



The literal "1" has type int. Probably "1ul" (if the compiler has 64-bit longs) or "1ull" was intended. [EDIT: Actually, the way the code is written, "1ull" was intended for sure.]

I am very careful to always use literals with the type I intend, since I was bitten by this one:
double limit_price = order.is_market() ? 0 : order.limit_price();
At the time when I wrote this code Order::limit_price() used to return a double, and all was good. Then someone changed that to return a type Price, which provided an implicit conversion to double. The person doing that is a C++ expert, he tested the code, and it worked fine. Then we changed compilers from gcc-2.95.2 to gcc-3.4 (yeah, this is years ago), and then prices started to get truncated to integer values. This was not caught when testing the new compiler version (because someone messed up), and we ended up sending incorrect reports to a market regulator for several days. I spent about two weeks cleaning up the mess.

It turns out the C++ standard specifies that truncating to int is the correct behavior. Recent versions of g++ will warn about this, but it took them a long time to do so.

Since then, I write 0.0 if I intend it to be a double, 0.0f if I intend it to be a float, etc. I also don't like implicit conversions and I would much rather have an explicit Price::as_double() method that I need to call explicitly.
I spend a lot of time reading code, probably more than most. One of the few things that causes my brain to short-circuit for a while when doing this, is usage of 0 instead of NULL. For this reason, I have used NULL. Thankfully, so have most of the people whose code I have to read.

This is probably just learned preference, but thankfully we now do have somthing better, something that I have yearned for for a long time - a type safe null pointer!

Thanks Bjarne & co.

0 is an int. If the intent is to call the pointer version, you should write a cast, or it won't work, whether you use NULL or 0. Once you use the cast, the intent is clear.

foo(0); // I want to call foo(int)
foo((int *)0); // I want to call foo(int*)

How is that not clear?

Because 0 as a literal constant (or anything that results in a 0 constant) can be implicitly cast to any built-in type.
So 0 is not necessarily an [color=#000080]int.

The problem with writing a cast explicitly every time you want to make your intent clear is that it leaves you with a whole lot of typing and thought. You have to cast to the correct type every time whereas just using NULL retains the same intent while keeping the coding process to a minimum.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

This topic is closed to new replies.

Advertisement