In C, NULL
is defined as (void *)0
whereas in C++ it is 0
. Why is it so?
In C I can understand that if NULL
is not typecast to (void *)
then compilers may/may not generate warning. Other than this, is there any reason?
Back in C++03, a null pointer was defined by the ISO specification (§4.10/1) as
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero.
This is why in C++ you can write
int* ptr = 0;
In C, this rule is similar, but is a bit different (§6.3.2.3/3):
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.
Consequently, both
int* ptr = 0;
and
int* ptr = (void *)0
are legal. However, my guess is that the void*
cast is here so that statements like
int x = NULL;
produce a compiler warning on most systems. In C++, this wouldn't be legal because you can't implicitly convert a void*
to another pointer type implicitly without a cast. For example, this is illegal:
int* ptr = (void*)0; // Legal C, illegal C++
However, this leads to issues because the code
int x = NULL;
is legal C++. Because of this and the ensuing confusion (and another case, shown later), since C++11, there is a keyword nullptr
representing a null pointer:
int* ptr = nullptr;
This doesn't have any of the above problems.
The other advantage of nullptr
over 0 is that it plays better with the C++ type system. For example, suppose I have these two functions:
void DoSomething(int x);
void DoSomething(char* x);
If I call
DoSomething(NULL);
It's equivalent to
DoSomething(0);
which calls DoSomething(int)
instead of the expected DoSomething(char*)
. However, with nullptr
, I could write
DoSomething(nullptr);
And it will call the DoSomething(char*)
function as expected.
Similarly, suppose that I have a vector<Object*>
and want to set each element to be a null pointer. Using the std::fill
algorithm, I might try writing
std::fill(v.begin(), v.end(), NULL);
However, this doesn't compile, because the template system treats NULL
as an int
and not a pointer. To fix this, I would have to write
std::fill(v.begin(), v.end(), (Object*)NULL);
This is ugly and somewhat defeats the purpose of the template system. To fix this, I can use nullptr
:
std::fill(v.begin(), v.end(), nullptr);
And since nullptr
is known to have a type corresponding to a null pointer (specifically, std::nullptr_t
), this will compile correctly.
Hope this helps!