In C programming, we typically use NULL, but in modern C++ we encounter both NULL and nullptr. C++11 introduced nullptr to eliminate the ambiguity that arises when NULL is used as a null pointer constant. This article explains the practical differences and why modern C++ code should prefer nullptr.
1. NULL in C #
In C, NULL is commonly defined as:
#define NULL ((void *)0)
This means NULL is literally a null pointer. Assigning it to any pointer type is valid because C allows implicit conversions from void*:
int *pi = NULL;
char *pc = NULL;
All valid in C.
2. NULL in C++ #
C++ is strongly typed, and implicit conversion from void* to another pointer type is not allowed. For this reason, C++ headers typically redefine NULL:
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
Thus, in C++, NULL is just the integer literal 0.
Why is this a problem? #
Because in C++, 0 is an integer, and this creates ambiguity when overloading functions.
Example:
#include <iostream>
using namespace std;
void func(void* p) {
cout << "func(void*)\n";
}
void func(int i) {
cout << "func(int)\n";
}
int main() {
func(NULL);
func(nullptr);
}
Output: #
func(int)
func(void*)
func(NULL) calls the int version because NULL is 0, an integer.
This is completely opposite to what the programmer usually intends.
3. nullptr in C++11 and Later #
C++11 introduced nullptr, a keyword that represents a true null pointer constant in all contexts.
- It is not an integer.
- It can convert to any pointer type.
- It avoids ambiguity in overload resolution.
From the earlier example:
func(NULL)→ callsfunc(int)func(nullptr)→ callsfunc(void*)✔ Correct
Why nullptr solves the issue
#
Unlike NULL (integer 0), nullptr has its own type: std::nullptr_t.
It is designed only to act as a null pointer.
4. How nullptr Was Emulated Before C++11 #
Before C++11, libraries sometimes implemented a class to mimic nullptr:
const class nullptr_t {
public:
template<class T>
operator T*() const { return 0; }
template<class C, class T>
operator T C::*() const { return 0; }
private:
void operator&() const; // forbid taking address
} nullptr = {};
This “fake nullptr” could convert to any pointer type but was still safe from integer overload issues.
5. ( (void*)0 ) and the Meaning of void
#
-
In C,
0can mean:- integer 0
- null character
- false
- null pointer
To avoid ambiguity, C defines NULL as:
#define NULL ((void *)0)
This explicitly casts 0 into a null pointer.
About void*
#
void*is a typeless pointer.- It can store the address of any pointer type.
- It cannot be dereferenced without casting.
Example:
double d = 3.14;
void *v = &d;
printf("%f\n", *((double*)v));
But: #
void *vptr;
double *dptr;
dptr = vptr; // ❌ Error in C++, implicit cast from void* not allowed
C++ forbids this implicit conversion for type safety.
✅ Conclusion #
NULL #
- In C++ is defined as 0
- Behaves like an integer
- Causes overload ambiguity
- Should be avoided in modern C++
nullptr #
- Introduced in C++11
- Has type
std::nullptr_t - Converts safely to any pointer
- Eliminates ambiguity
- The recommended choice for all modern C++ code
Use nullptr, avoid NULL. It improves clarity, correctness, and type safety.