nullptr in C++



The nullptr keyword in C++ represents a null pointer value which was earlier represented using NULL or 0. It was introduced in C++11 and is of type std::nullptr_t. The nullptr is a type-safe pointer that is implicitly convertible and can be compared to any pointer.

The syntax for defining a nullptr is given below −

int* ptr = nullptr; // ptr is a null pointer of type int*

Example

The following example demonstrates how to use nullptr in C++

#include <iostream>
using namespace std;

int main() {
	int *ptr = nullptr; // ptr is a null pointer

	if (ptr == nullptr)
	{
		cout << "Pointer is null." << endl;
	}
	else
	{
		cout << "Pointer is not null." << endl;
	}

	int x = 10;
	ptr = &x;

	if (ptr != nullptr)
	{
		cout << "Pointer now points to: " << *ptr 
             << endl;
	}

	return 0;
}

The output of the above code is given below −

Pointer is null.
Pointer now points to: 10

Why Do We Need nullptr?

We need nullptr because of the following problems caused by using NULL or 0

Ambiguity in Function Calling

Calling a function with value '0' to represent NULL creates an ambiguity because compiler treats '0' as an integer value rather than a null pointer.

In this example, we are calling display() function by passing value as '0'. It calls the function that has an int parameter rather than calling the function that has a pointer in its parameter.

#include <iostream>
using namespace std;

void display(int n){
   cout << "Calling display function with int" << endl;
}

void display(int *p){
   cout << "Calling display function with int*" << endl;
}

int main(){
   display(0); // Ambiguous call as Compiler treats 0 as integer
   return 0;
}

The output of the above code is given below −

Calling display function with int

Solution: Below is the solution to above problem where we have used nullptr that calls the display() function that has a pointer in its parameter −

#include <iostream>
using namespace std;

void display(int n){
   cout << "Calling display function with int" << endl;
}

void display(int *p){
   cout << "Calling display function with int*" << endl;
}

int main(){
   display(nullptr); 
   return 0;
}

The output of the above code is given below −

Calling display function with int*

Problem in Function Overloading

If you call an overloaded function with NULL value, it will cause a compilation error. The NULL is valid for both parameters that is int and int*. So, in this confusion compiler throws an error. Below is an example to demonstrate this problem.

#include <iostream>
using namespace std;

class Demo{
public:
	void show(int n)
	{
		cout << "Calling show() function with int" << endl;
	}

	void show(int *p)
	{
		cout << "Calling show() function with int*" << endl;
	}
};

int main() {
	Demo obj;
	// Ambiguous call with NULL
	obj.show(NULL);
	return 0;
}

The output of the above code is given below −

main.cpp: In function 'int main()':
main.cpp:22:17: error: call of overloaded 'show(NULL)' is ambiguous
   22 |         obj.show(NULL);
      |         ~~~~~~~~^~~~~~
main.cpp:7:14: note: candidate: 'void Demo::show(int)'
    7 |         void show(int n)
      |              ^~~~
main.cpp:12:14: note: candidate: 'void Demo::show(int*)'
   12 |         void show(int *p)
      |              ^~~~

Solution: Below is the solution to above problem where we have used nullptr that calls the display() function that has a pointer in its parameter −

#include <iostream>
using namespace std;

class Demo {
public:
	void show(int n)
	{
		cout << "Calling show() function with int" << endl;
	}

	void show(int *p)
	{
		cout << "Calling show() function with int*" << endl;
	}
};

int main() {
	Demo obj;
	obj.show(nullptr); // Correctly calls int* function
	return 0;
}

The output of the above code is given below −

Calling show() function with int*

Type-Safety Problem

The NULL is treated as an integer since it is defined with '0'. It creates the same ambiguity as the above two problems. It is solved using nullptr because nullptr is type-safe unlike NULL and it is implicitly convertible to any pointer type.

#include <iostream>
using namespace std;

int main() {
	int* ptr = nullptr;

	cout << "Comparing with NULL" << endl;
	if (ptr == NULL) {
		cout << "NULL is TRUE" << endl;
	} else {
		cout << "NULL is FALSE" << endl;
	}

	cout << "\nComparing with nullptr" << endl;
	if (ptr == nullptr) {
		cout << "nullptr is TRUE" << endl;
	} else {
		cout << "nullptr is FALSE" << endl;
	}

	int value = 0;

	// NULL will be treated as integer 0
	if (value == NULL) {
		cout << "value(0) = NULL is TRUE" << endl;
	}
	return 0;
}

The output of the above code is given below −

main.cpp: In function 'int main()':
main.cpp:24:22: warning: NULL used in arithmetic [-Wpointer-arith]
   24 |         if (value == NULL) 
      |                      ^~~~
Comparing with NULL
NULL is TRUE

Comparing with nullptr
nullptr is TRUE
value(0) = NULL is TRUE

NULL vs nullptr

The difference between NULL and nullptr is mentioned in the table below −

NULL nullptr
It is an integer constant that represents either 0 or 0L. In C, it is represented as ((void*)0). It is a pointer literal of type std::nullptr_t.
It is not type-safe as it may be treated as an int. It is type-safe as it represents only pointer.
It can be assigned to int values. It can not be assigned to an int value, as it will show an error.
In function overloading, there is ambiguity when calling the function. It prevents the problem of ambiguity in function overloading.
Example: int* p = NULL; Example: int* p = nullptr;

Nullptr Use Cases

The main purpose of the nullptr is to assign a null value to any pointer. Here are some use cases where a nullptr can be used.

Resetting Pointers After Deletion

A pointer needs to be assigned to a null value after deletion to avoid a dangling pointer. Here is an example to reset pointer to null after cleaning up the memory.

#include <iostream>
using namespace std;

int main() {
	int *ptr = new int(57);
	cout << "Value: " << *ptr << endl;

	delete ptr;    // Free memory
	ptr = nullptr; // Resetting pointer

	if (ptr == nullptr)
		cout << "Pointer reset successful." << endl;

	return 0;
}

The output of the above code is given below −

Value: 57
Pointer reset successful.

Checking Pointer Validity

You should first check that the pointer is not null before accessing it to avoid crashes. Here is an example to check if the pointer ptr is a null pointer or not.

#include <iostream>
using namespace std;

int main() {
	int *ptr = nullptr;
	if (ptr != nullptr)
		cout << "Pointer value: " << *ptr << endl;
	else
		cout << "It is a null pointer." << endl;

	return 0;
}

The output of the above code is given below −

It is a null pointer.	

Safe Object Initialization for Null Value

The nullptr can be used for setting a pointer to null without causing any error or any garbage value. Here is an example to assign a null value to represent an empty linked list

#include <iostream>
using namespace std;

struct Node {
	int data;
	Node *next;
};

int main() {
	Node *head = nullptr; // Empty linked list

	if (head == nullptr)
		cout << "Linked list is empty." << endl;

	return 0;
}

The output of the above code is given below −

Linked list is empty.

Function Overloading Resolution

The nullptr is also used to resolve the ambiguity in function overloading. Here is an example.

#include <iostream>
using namespace std;

void show(int n){
   cout << "Integer called." << endl;
}

void show(int *p){
   cout << "Pointer called." << endl;
}

int main(){
   show(nullptr); // Calls pointer
   return 0;
}

The output of the above code is given below −

Pointer called.

Conclusion

In this chapter, we have understood that the nullptr in C++ is used when we need a null pointer. Before C++11, NULL was used, but it had various problems which is addressed by the nullptr. We have also discussed various use cases of nullptr with examples.

Advertisements