NULL vs 0

Started by
43 comments, last by wqking 11 years, 4 months ago
I would say it depends, but for things that remain mostly static, I use 0, BUT keep everything to do with it well commented.

C dominates the world of linear procedural computing, which won't advance. The future lies in MASSIVE parallelism.

Advertisement

But if you are absolutely intent on avoiding macros, use a constant:
const size_t MyNull = 0;


Ew, please no. I'd rather have someone just use 0 instead of a global variable like that, because at least 0 conveys more information than MyNull does (for example, can I trust that if I set a pointer to MyNull, that if (ptr) will be false?)
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
I personally preferred 0 till nullptr came around. There was no overwhelming reason, it just seemed to read better IMO.
0 obviously gives the correct result, because NULL is defined as 0. The reason people use NULL over 0 is because it's self documenting -- a in-source comment saying this is a "zero pointer", whereas the literal "0" means many things depending on context. Generally, I like self-documenting code like this...

However, IMHO, there's usually enough context around to know whether [font=courier new,courier,monospace]myVar = 0[/font] means "set [font=courier new,courier,monospace]myVar[/font] to a null pointer value" or "set [font=courier new,courier,monospace]myVar[/font] to the integer value of zero", so I don't really see the need for the macro.

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

0 because I never know what header has the definition for NULL.
I'm in the same boat.
I like the fact that [font=courier new,courier,monospace]nullptr[/font] is now a part of the language, so it works everywhere... but [font=courier new,courier,monospace]NULL[/font] has the problem that it's not part of the language; if you don't include some specific header (possibly one of many headers that conditionally define it), then this "keyword" doesn't exist.
I don't like having to include unnecessary headers, so I instead evaluate the macro in my head and write [font=courier new,courier,monospace]0[/font].

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 assert() will be triggered since mask will be 0.

I prefer self-documenting code and always use NULL. It is theoretical that NULL could be defined as something other than 0 but:
#1: It wouldn’t change the integrity of my code as long as I am always using NULL to check for invalid pointers as apposed to mixing between if ( ptr == NULL ) and if ( !ptr ).
#2: 0 is the only literal constant that is defined by the standard to be implicitly castable to a pointer of any kind, thus the idea that NULL could be something other than 0 is only theoretical, and anyone who ends up in such a situation brought it upon him- or her- self.


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

I used to use NULL, now it's nullptr.

Stefano Casillo
TWITTER: [twitter]KunosStefano[/twitter]
AssettoCorsa - netKar PRO - Kunos Simulazioni


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



I'll bite. The first line is undefined behavior on your average system where [font=courier new,courier,monospace]int[/font] is 32 bits, seeing as [font=courier new,courier,monospace]1 << 48[/font] is interpreted and evaluated as an [font=courier new,courier,monospace]int[/font], and shifting by more than 31 bits is undefined behavior on a 32-bit data type. The type of [font=courier new,courier,monospace]0x1000000000000[/font] will be [font=courier new,courier,monospace]long long[/font] (again, assuming your average system), but the comparison will be meaningless because of the previous undefined behavior (and as L. Spiro points out, [font=courier new,courier,monospace]mask[/font] will likely be zero).

And for the thread: I always used [font=courier new,courier,monospace]NULL[/font] because I found it to be a bit more self documenting than 0, but I also hated the fact that it was a macro and not built into the language's syntax. So I guess my official answer is: I always used [font=courier new,courier,monospace]NULL[/font], but I also always hated it.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
Although NULL IS ugly, it cannot be replaced with 0 (like others have explained). But you can always "rename" stuff in C/C++, so while it has been long since I last came across a compiler without support for nullptr, I would just redefine NULL as nullptr since I would probably also build the source on C++0x/C++11 complaint compilers, Of course, you loose the type of nullptr when compiling for pre-C++0x (std::nullptr_t) but with a little care (only load/store and compare with pointers), I cannot see why it would cause trouble.

#ifdef THIS_COMPILERS_VERSION_IS_WAY_TOO_OLD_FOR_MY_TASTE
#define nullptr NULL
#endif


EDIT: An enhanced version might look like this, since it does not pollute the global namespace and even gives nullptr a type:

namespace SuperLibrary
{
#ifdef THIS_COMPILERS_VERSION_IS_WAY_TOO_OLD_FOR_MY_TASTE
const void *nullptr = (unsigned)NULL;
#endif
...
}
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.

#2: 0 is the only literal constant that is defined by the standard to be implicitly castable to a pointer of any kind,

No, any integer constant expression that evaluates to zero is a valid null pointer constant. This includes 0 but also includes (1-1), (0xful >> 10), and '\0'. The standard specifically lists both 0 and 0L as two legal options for NULL. The latter is used by some compilers where an int is not the same size as a pointer, but a long is. This can give narrowing conversion warnings when trying to assign a NULL to a regular int. See the previously mentioned sections 8.1 in C++98/C++03 and 8.2 in C++11, as well as sections 4.10 and 5.19 (same number for all three).


I would just redefine NULL as nullptr since I would probably also build the source on C++0x/C++11 complaint compilers, Of course, you loose the type of nullptr when compiling for pre-C++0x (std::nullptr_t) but with a little care (only load/store and compare with pointers), I cannot see why it would cause trouble.

Again, as previously mentioned, NULL has an integer type which does not play happily with things like function overloads and template type deduction. If you have an overloaded function void foo(int) and void foo(void *), foo(NULL) calls the first overload but foo(nullptr) calls the second overload. Similarly if you have a vector<int *>, std::fill(vec.begin(), vec.end(), NULL) will fail to compile but std::fill(vec.begin(), vec.end(), nullptr) should.


const void *nullptr = (unsigned)NULL;

This is not a valid replacement for the C++11 nullptr. First, as previously mentioned, a void pointer cannot be assigned to other pointer types without an explicit cast. Basic usage such as int * a = nullptr; would fail. Second, this isn't a constant expression. You've defined nullptr as a non-const pointer to const void. Presumably you wanted some variation with const on the right hand side of the * to make the pointer itself const. But also, even when dealing with void pointers you can still get into a situation where you need a cast because a const void * is not assignable to a volatile void * without a cast.

This topic is closed to new replies.

Advertisement