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?
3 Answers 3
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.
3 Comments
0
optionally cast to void*
.NULL
must be (void*)0
in C. This isn't the case. Unfortunately it may be defined to just any null pointer constant, so it is not a very reliable concept in C. Therefore I personally think that NULL
should just be avoided. And the reason some seem to prefer it to be of pointer type are more the rules for implicit argument promotion to functions without prototype, namely va_arg argument lists. But this is another story...In C, NULL
expands to an implementation-defined "null pointer constant". A null pointer constant is either an integer constant expression with the value 0, or such an expression cast to void*
. So a C implementation may define NULL
either as 0
or as ((void*)0)
.
In C++, the rules for null pointer constants are different. In particular, ((void*)0)
is not a C++ null pointer constant, so a C++ implementation can't define NULL
that way.
Comments
The C language was created to make it easier to program microprocessors. A C pointer is used to store the address of data in memory. A way was needed to represent that a pointer had no valid value. The address zero was chosen since all microprocessors used that address for booting up. Since it couldn't be used for anything else zero was a good choice to represent a pointer with no valid value. C++ is backward compatible with C so it's inherited that convention.
The requirement of casting zero when used as a pointer is only a recent add on. Later generations of C wanted to have more rigor (and hopefully fewer errors) so they started being more pedantic about syntax.
19 Comments
0
to a pointer type. Neither C nor C++ have such a rule. In C you may have (void*)0
as a null pointer constant, but nothing is forcing you to do so.0
in source is required to be a null pointer constant - but that doesn't require the internal representation of the null pointer to be all bits zero, nor for it to be the same as the representation of integer zero.*(int*)0 == 0
. And writing code that doesn't assume a null pointer is all-bits-zero really isn't that difficult.
void*
to any pointer type, and in C++ you cannot.NULL
as0
. A C implementation can define it as((void*)0)
, but not as(void*)0
. A macro defined in a standard C headers must be protected by parentheses so it can be used as if it were a single identifer; with#define NULL (void*)0
, the expressionsizeof NULL
would be a syntax error.