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
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.NULLas0. 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 NULLwould be a syntax error.