Skip to main content

NULL vs nullptr in Modern C++: What Developers Should Know

·537 words·3 mins
C C++ NULL Nullptr
Table of Contents

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) → calls func(int)
  • func(nullptr) → calls func(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, 0 can 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.

Related

Mastering C++ Exception Handling for Robust Code
·718 words·4 mins
C++ Exception Handling RAII
嵌入式 C 程序的内聚和耦合
·306 words·2 mins
程序 C Cohesion Coupling Embedded
Debugging Memory Overwrite Issues in Embedded C
·601 words·3 mins
C Embedded Memory Debugging