nullptr (null pointer) in C++

Do not miss this exclusive book on Binary Tree Problems. Get it now for free.

A null pointer (nullptr) is a pointer that does not point to any memory location. nullptr is a keyword that was introduced in C++11 standard to denote a null pointer. This standard also introduced a nullptr_t type. The rest of this article dives into the reasons for introduction of the new keyword, looks further into nullptr, and provides some examples and a use-case.

Before C++11

Many of the features that C++ has, have been taken from C. In C a null pointer is defined as (void *)0. Which is an integer 0 typecasted to a void*. The NULL macro which is defined usually as 0 (sometimes as (void*)0 or 0L) was also used to denote a null pointer.

So before C++11, the only way to refer to a null pointer was by using the integer value 0 or some typecasted form of 0, which is quite ambiguous with respect to whether a pointer or integer is intended. Thus when the NULL macro was used, the underlying value was still 0.

This posed several problems in C++, we'll go through some of them.

Problems

Implicit conversion

char *c = NULL;   // Implicit conversion from void* to char*
int i = NULL;     // works, but i is not a pointer type

here the variable i will be assigned the value of 0, but is not intended. This shows that it is not type safe to use it.

Function calls can be ambiguous

Consider the following example

void func(int);
void func(double *);
int main()
{
  func(0);
}

In the main() function, the function func() with a parameter of 0 is called. But given that the value of 0 is considered to be both an integer literal and the null pointer constant, which function is actually executed? Is it the function func(int) or is it func(double *)?
In this example the func(int) will be executed but the programmer might have intended to execute func(double*). This is ambiguous because 0 is also a null pointer constant.

C++11

nullptr

Simply put, a value of 0 should mean an integer literal. The introduction of nullptr keyword allows just that. The nullptr constant cannot be implicitly converted to an integer, but it can be converted to any other pointer type. This allows C++ to improve its support for generic programming by allowing it to express both integer constant 0 and nullptr constant unambiguously.

But what about the NULL macro? In C++11 the NULL macro is also defined to be nullptr.

#define NULL nullptr  // from C++11

This would allow better typechecking, and distinguishes the use of NULL as a null pointer (as intended) and not as the value 0.

nullptr simplifies its usage by allowing comparison. To test if a pointer is a nullptr, you can compare it by using the = and the != operator. This is useful when we are using pointers to access objects. Since it is not possible to access data through a null pointer, a check ptr == nullptr or ptr != nullptr is therefore required.

nullptr_t

The C++11 standard also introduced a new type nullptr_t. This is the type of the nullptr literal. Note that nullptr_t is not a reserved keyword; It's a typedef defined in the <cstddef> header

typedef decltype(nullptr) nullptr_t;

But why a new type? One of the reasons is the implicit conversion of the nullptr to any pointer type. This creates confusion in function overloading. To clarify this, the following example illustrates the reason.

Consider the example code where the func() is overloaded.

void func(int*);
void func(double*);

void main(){
    func(nullptr); // error: call of overloaded 'func(std::nullptr_t)' is ambiguous
}

It would be ambiguous as to which of the functions above is being called since they both are candidates. nullptr can be converted to both int* and double*. The compiler cannot decide and throws an error.

Use of nullptr_t would solve the problem and allow us to catch the nullptr parameter by the function.

void func(int*);          // first
void func(double*);       // second
void func(nullptr_t);     // third

void main(){
    func(nullptr); // OK, calls the third function
}

to call the first and the second function with nullptr we would now have to typecast it as (int*)nullptr and (double*)nullptr respectively.

Simple examples

Simple usage

double *k = nullptr;

here k is a null pointer. Pointers are not initialized automatically. Before initialization they may contain some random bit-pattern. So it is best practice to initialize the pointer to nullptr when it is being defined. This also allows us to later check if the pointer is nullptr to avoid accidentally accessing a null pointer.

Some simple operations

double *d = nullptr; // It can be converted to any pointer or pointer-to-member type.

assert(d == nullptr); // OK

int k = nullptr; // error: cannot convert 'std::nullptr_t' to 'int' in initialization 

int v = 55 + nullptr; // error: nullptr cannot be used in an arithmetic expression.

Use case

nullptr is useful wherever NULL is expected. We'll go through a simple linked list example where nullptr is useful.

Linked list

A simple application is the linked list. A linked list is a set of nodes, each of which have data and a link to the next node. The link is usually a pointer that points to the next node.
Here instead of using the NULL macro, we can use the nullptr
Consider a sample Node structure

struct Node
{
    int data;
    Node *next;
    Node(int data, Node *next = nullptr): data(data), next(next)
    {
    }
}

In this the Node constructor assigns the address of the next Node to the next pointer. The default value for this is nullptr.

To traverse to the last element in the linked list, we could write a check like

while(current->next != nullptr)
{
    current = current->next;
}

Conclusion

As we've seen, the introduction of nullptr has eliminated some ambiguities and improved support for generic programming in C++ by allowing it to express both integer 0 and null pointer unambiguously.

Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.