AlgoMaster Logo

nullptr (C++11)

Medium Priority16 min readUpdated September 21, 2026
Listen to this chapter
Unlock Audio

nullptr is the C++11 keyword for "this pointer points to nothing". It replaces the older habits of writing 0 or the macro NULL, which both predate C++ and cause real bugs once function overloading enters the picture. This chapter covers what nullptr is, why it exists, how it compares to its predecessors, and the small set of idioms that go with it.

A Pointer Pointing at Nothing

Every pointer holds an address. Most of the time, that address belongs to a real object somewhere in memory, and reading or writing through the pointer works. But there are situations where a pointer variable exists with no object yet to point at:

  • A "find product by id" function that didn't find anything.
  • A linked list whose next field marks the end of the chain.
  • An optional discount rule that isn't set on this order.
  • A pointer member of a struct that gets filled in later.

For all of these, the pointer needs a value that means "I'm not pointing at anything". That value is nullptr.

p is a valid int*. It can be copied, passed around, compared, and reassigned. The one thing that doesn't work is dereferencing it. Writing *p when p is nullptr is undefined behavior, and on most systems it produces a segmentation fault. The rule: check before dereferencing.

The address that gets printed is implementation-defined. Most compilers print 0, some print 0x0 or a similar representation. The important part is that nullptr compares equal to itself and unequal to any address of a real object.

"Implementation-defined" means the C++ standard lets each compiler choose the behavior, but the compiler has to document it. That's different from "undefined behavior", which is the standard giving up entirely. Both appear in this chapter.

The Problem with NULL and 0

Before C++11, there was no dedicated null-pointer keyword. Code used one of two things:

  • The integer literal 0, which is implicitly convertible to any pointer type.
  • The macro NULL, defined in <cstddef> (and several other standard headers) as either 0 or ((void*)0), depending on the compiler.

Both look like they should work, and for simple assignments they do:

All three lines compile and produce the same result: a pointer that doesn't point anywhere. So why did C++11 add a new keyword if the old syntax already worked?

The trouble shows up the moment function overloading enters the picture. Consider an e-commerce app with two log functions, one for an error code and one for a pointer to a Product:

Read that output carefully. log(0) calls the int overload, which is fine if the integer zero was intended, but wrong if a null pointer was meant. log(NULL) does the same thing on most compilers, because NULL is 0 in disguise. The compiler sees an integer literal and picks the integer overload. log(nullptr) is the only call that does what a human reading the code would expect: it picks the pointer overload, because nullptr has a pointer-like type that cannot match int.

The NULL-picking-the-wrong-overload bug is silent. The program compiles, runs, and produces output. It just doesn't do what the author intended. With g++ a -Wconversion-null warning may help, but warnings are easy to miss. nullptr removes this category of bug entirely.

This is the original reason nullptr was added in C++11. The committee wasn't replacing NULL for style points. They were fixing an overload-resolution hole that had bitten C++ code for two decades.

What nullptr Is

nullptr is a keyword in the C++ language, added in C++11. It is not a macro, not a constant in a header, not a global variable. It's part of the grammar.

Its type is std::nullptr_t, declared in <cstddef>. The type rarely appears in user code, but it shows up in two places:

  1. As the type of the expression nullptr itself.
  2. As a parameter type when a function wants to accept only the literal nullptr.

The defining trait of std::nullptr_t is that it implicitly converts to any pointer type, and to no other type. Specifically:

  • nullptr converts to int*, double*, Product*, void*, any function pointer, any member pointer.
  • nullptr does not convert to int, bool (in overload resolution at least), long, or any other arithmetic type.

That last point is what makes the overload resolution clean. The compiler can never confuse nullptr with an integer, because the conversion to integer simply doesn't exist.

The commented-out line is the interesting one. Uncommenting int n = nullptr; causes a compile error. With g++ the message is something like error: cannot convert 'std::nullptr_t' to 'int' in initialization. Clang says nearly the same thing. The error is by design, and it's what prevents the overload-resolution problem shown earlier.

A small exception: nullptr can convert to bool (as false), but only in a context that explicitly asks for a bool, like the condition of an if statement. It will not convert to bool to break a tie in overload resolution. The practical effect is that if (p) and if (!p) work as expected for any pointer, including a nullptr-valued one.

Idioms: Comparing, Assigning, Testing

Three patterns make up almost all the everyday use of nullptr.

1. Assign `nullptr` to mean "no object yet" or "no longer valid".

Assigning nullptr to a pointer marks it as unused. Later code can check whether the pointer is set without needing a separate bool isSet flag next to it.

2. Compare against `nullptr` to test "is this pointer set?"

The comparison reads cleanly. Most C++ style guides recommend == nullptr over the shorter forms when the check should stand out. The shorter forms exist too, and they're idiomatic:

3. Use the pointer directly in a boolean context.

if (p) is shorthand for if (p != nullptr), and if (!p) is shorthand for if (p == nullptr). The compiler converts the pointer to bool: any non-null pointer is true, nullptr is false. Some teams prefer the explicit == nullptr form because it's louder; others prefer the short form because it reads like English. Either is fine. Pick one and stick with it.

A realistic example tying these together. The function returns a pointer to a Product if one matches, or nullptr if not:

Two calls to the same function, two different outcomes. The caller always checks the returned pointer before dereferencing it. The null check is the contract: returning nullptr is the function's way of saying "no result", and the caller has to handle that case before using the pointer.

A null check is a single comparison and a conditional branch. The branch is almost always predicted correctly by the CPU when one outcome dominates, so the cost is effectively zero in practice. Skipping the check to "save time" is never the right trade.

What "Null Pointer" Means Conceptually

A regular pointer holds an address that maps to a real object somewhere in memory. A null pointer holds a value reserved to mean "no object". The exact bit pattern doesn't really matter; what matters is that the language and runtime treat it as different from any valid address.

The diagram shows three states a pointer can be in. The first is the normal case: the pointer holds the address of a real object, and dereferencing it reads or writes that object. The second is nullptr: the pointer holds a sentinel value that means "no object", and dereferencing it is undefined behavior. The third is the dangerous case: the pointer holds an address that was once valid but isn't anymore, because the object has been destroyed. The runtime can't tell the difference between the first and third cases, which is what makes dangling pointers dangerous. nullptr is at least explicit: there's nothing here, check before reading.

The practical takeaway: nullptr is a deliberate, safe "empty" value. It can be read, written, compared, and copied without crashing. The only thing that doesn't work is going through it to an object that isn't there.

nullptr and Function Overloading: A Closer Look

The overload example from earlier was the headline reason for adding nullptr. A useful variation comes up in real code: a function that overloads on different pointer types.

The commented-out line notify(nullptr) is interesting. Both overloads accept a pointer, and nullptr converts to both Product* and Customer* equally well. The compiler has no basis to pick one over the other, so it gives an ambiguity error. Uncommenting the line with g++ produces a message like error: call of overloaded 'notify(std::nullptr_t)' is ambiguous.

This is correct behavior, not a bug. The fix is to tell the compiler which pointer type to use via a cast. static_cast<Product*>(nullptr) produces a null Product*, which matches the first overload unambiguously.

A function that takes std::nullptr_t directly is rare but legal. It can be used to forbid passing a real pointer to a function, or to provide a "null" overload that does something specific. Some standard-library smart-pointer constructors use this technique.

Common Mistakes

A few mistakes show up repeatedly with nullptr. Each one is small, but they're worth recognizing.

Mistake 1: Mixing `nullptr` and `NULL` in the same codebase.

Both compile, both produce a null pointer, but the inconsistency makes the code harder to read and review. If anyone adds an integer overload later, the NULL line changes meaning without warning while the nullptr line stays correct. Pick nullptr everywhere and treat NULL as a sign of old code that needs updating.

Mistake 2: Dereferencing without checking.

If findProductById can return nullptr, every caller must handle that case. The fix is to test the pointer before going through it. This is a common source of crashes in C++ programs, and the habit of "check, then dereference" prevents it.

Mistake 3: Writing `if (p == 0)` instead of `if (p == nullptr)`.

The first form compiles and works (the 0 is treated as a null pointer constant in that context), but it reads as a numeric comparison. if (p == nullptr) makes the intent clear. Modern style guides, including the C++ Core Guidelines, recommend the nullptr form.

Mistake 4: Returning `0` instead of `nullptr` from a function that returns a pointer.

return nullptr; is clearer and more consistent with how the rest of modern C++ talks about null pointers. The two compile to the same code; the difference is for humans.

A Brief Word on Smart Pointers

The chapters on dynamic memory (later in this section) introduce raw new and delete. Modern C++ generally prefers smart pointers from the standard library. The short version, for context: smart pointers like std::unique_ptr<T> and std::shared_ptr<T> default to a nullptr state when default-constructed.

Smart pointers use the same null idioms as raw pointers: compare with == nullptr, test in a boolean context, assign nullptr to clear. The mechanics underneath are different (the smart pointer is also managing ownership and destruction), but the surface-level interaction with null is identical.

Modern Style: Always nullptr

The short style guide is:

  • For new code, always write nullptr. Never 0, never NULL.
  • When updating old code that uses NULL, replace it with nullptr whenever you touch it.
  • Test pointers with == nullptr, != nullptr, if (p), or if (!p). All four are idiomatic.
  • Never compare a pointer to the integer 0 or to NULL in new code; the nullptr forms are clearer and safer.

nullptr is one of the smallest changes C++11 introduced, and it's also one of the easiest to adopt. Every compiler that supports C++11 supports it (which has been every major compiler for over a decade), and there's no runtime cost. The only thing it changes is how readable and overload-safe null-pointer handling becomes.

Quiz

nullptr Quiz

10 quizzes