What Exactly nullptr Is in C++?
The answer to “What exactly nullptr is in C++?” would be a piece of cake for experienced C++ eyes & for those who are aware of Modern C++ i.e. keyword. But nullptr is more than just a keyword in C++ & to explain that, I have written this article. But before jump-into it, we will see issues with NULL & then we’ll dive into the unsophisticated implementation of nullptr & some use-cases of nullptr .
Why do we need nullptr?
To distinguish between an integer 0(zero) i.e. NULL & actual null of type pointer.
nullptr vs NULL
- NULL is 0 (zero) i.e. integer constant zero with C-style typecast to void* , while nullptr is prvalue of type nullptr_t which is integer literalevaluates to zero.
- For those of you who believe that NULL is same i.e. (void*)0 in C & C++. I would like to clarify that no it’s not:
- C++ requires that macro NULL to be defined as an integral constant expression having the value of 0 . So unlike in C, NULL cannot be defined as (void *)0 in the C++ standard library.
Issues with NULL
Implicit conversion
Function calling ambiguity
Compilation produces the following error:
Constructor overload
- In such cases, you need explicit cast (i.e., String s((char*)0)) .
Implementation of unsophisticated nullptr
- nullptr is a subtle example of Return Type Resolver idiom to automatically deduce a null pointer of the correct type depending upon the type of the instance it is assigning to.
- Consider the following simplest & unsophisticated nullptr implementation:
- If the above code seems strange & weird to you(although it should not), then I would suggest you go through my earlier article on advanced C++ concepts . The magic here is just the templatized conversion operator.
- If you are into a more authoritative source, then, here is a concrete implementation of nullptr from LLVM header .
Use-cases of nullptr
- As shown in the above example, when nullptr is being assigned to an integer pointer, a int type instantiation of the templatized conversion function is created. And same goes for method pointers too.
- This way by leveraging C++ template functionality, we are actually creating the appropriate type of null pointer every time we do, a new type assignment.
- As nullptr is an integer literal with value zero, you can not able to use its address which we accomplished by deleting & operator.
Function calling clarity with nullptr
- Now, func( int* ) will be called as nullptr will implicitly be deduced to int* .
Typecasting on nullptr_t
- A cast of nullptr_t to an integral type needs a reinterpret_cast , and has the same semantics as a cast of (void*)0 to an integral type.
- Casting nullptr_t to an integral type holds true as long as destination type is large enough. Consider this:
- A reinterpret_cast cannot convert nullptr_t to any pointer type. Use static_cast instead.
- nullptr is implicitly convertible to any pointer type so explicit conversion with static_cast is only valid.
nullptr_t is comparable
From Wikipedia article :
– …null pointer constant: nullptr . It is of type nullptr_t , which is implicitly convertible and comparable to any pointer type or pointer-to-member type.
– It is not implicitly convertible or comparable to integral types, except for bool .
Template-argument is of type std::nullptr_t
- As discussed earlier, Return Type Resolver needs an assignee to deduce the type.
Conversion to bool from nullptr_t
From cppreference :
– In the context of a direct-initialization , a bool object may be initialized from a prvalue of type std::nullptr_t , including nullptr . The resulting value is false. However, this is not considered to be an implicit conversion.
- The conversion is only allowed for direct-initialization , but not copy-intialization , which including the case for passing an argument to a function by value. e.g.
Summary by FAQs
Both true and false are keywords & literals, as they have a type ( bool ). nullptr is a pointer literal of type std::nullptr_t , & it’s a prvalue (i.e. pure rvalue , you cannot take the address of it using & ). For more .
– No function calling ambiguity between overload sets.
– You can do template specialization with nullptr_t .
– Code will become more safe, intuitive & expressive. if (ptr == nullptr); rather than if (ptr == 0); .
Not at all. The following line does not even compile:
Yes. But only if you direct-initialization . i.e. bool is_false
It’s just the templatized conversion operator known as Return Type Resolver .
What exactly nullptr is in C++?
References
You can find similar resources here , here , and in nullptr proposal(N2431) ; however, this post will walk you through the ins and outs of the spec step-by-step in a more friendly way so that you come away with a full understanding of the concept without any needless confusion
Do you like it☝️? Get such articles directly into the inbox…!?
Нулевые указатели (null и nullptr) в C++. Учимся ходить по граблям изящно

В этом материале для новичков мы рассуждаем про обнаружение в коде C++ распространенного дефекта «разыменование нулевого указателя», попутно объясняя его скрытую коварность.

1. Разыменование нулевого указателя
Сегодня рассмотрим причину дефекта в коде С++, который получается, если программа обращается по некорректному указателю к какому-то участку памяти. Такое обращение ведет к неопределенному поведению программы, что приводит в большинстве случаев к аварийному завершению. Данный дефект получил название разыменование нулевого указателя ( CWE-476 ). Мы поговорим о том, что такое NULL и nullptr и для чего они нужны.
По сути, это почти одинаковые вещи, но есть нюансы.
Язык С++ не имеет автоматического сборщика мусора, как, например, в Java или C#. Если мы выделяем область под данные, то никто кроме нас не позаботится о том, чтобы область памяти была очищена. Если в памяти находится одно число, это не является проблемой.
Однако, если, например, программа работает в цикле, используя миллион итераций, и в каждой итерации запрашиваются в операционной системе новое место под данные, то тут возможны проблемы. На каком-то этапе место в памяти заканчивается и наша программа аварийно завершает работу. Поэтому, чтобы избежать подобной ситуации, следует использовать delete для очистки неиспользуемых данных, указав указатель на область данных.
Если вы начинающий программист, возьмите себе за правило, каждый раз, как только вы используете оператор new, выделяя под что-то память, тут же (в этой же функции) писать deletе , чтобы потом не забыть сделать это. Это избавит вас от потенциальных проблем с утечкой памяти.
Итак, в нашем коде (пример выше) с помощью оператора new мы выделили место для нашей оперативной памяти. После очистки места, которое мы выделяли под данные в динамической части оперативной памяти, сами данные исчезают. В следствии действия оператора delete данные уничтожаются и система может выделять память, которую мы уже не используем, для любых других своих нужд.
Однако, у нас остается проблема! В нашем указателе *pa все еще сохранен адрес на тот участок памяти, где у нас лежали данные и, в принципе, нам никто не запрещает туда обращаться.

Мы можем туда что-нибудь записать или получить данные, которые там находятся. А можем что-нибудь повредить либо получить некорректные данные, далее невольно начать с ними работать если у нас есть подобная ошибка в логике. В нашем случае, например, мы можем по указателю получить вот такое число — 842150451 .
Короче говоря, надеюсь, вы поняли эту тонкость, что после деинициализации ранее занятой памяти наш старый указатель может продолжать указывать в удаленное место памяти, что потенциально может привести к краху программы.
2. Нулевое значение и нулевые указатели
Если мы ведем какие-то расчеты и вдруг получаем некоторое случайное число (не имеющее отношение к расчетам), мы можем даже не узнать, что что-то пошло не так. Неплохо было бы определиться и отметить указатель каким-то таким образом, чтобы он никуда не указывал (чтобы однозначно исключить патовую ситуацию описанную в примере выше).
Для этого и существуют NULL и nullptr . Обратите внимание, что если у нас сейчас вызывается оператор delete на нашем указателе (мы очищаем находящуюся по нему память), то оттуда данные теряются.
Если мы опять принудительно выведем на консоль эти данные (из освобожденного участка памяти), то в принципе, у нас может случиться чудо — мы увидим в консоли тот «мусор», который сейчас в памяти (куда указывает наш указатель, после того как мы его почистили).
Но, если мы еще раз возьмем и вызовем оператор delete pa , то все закончится очень плохо — мы увидим на экране сообщение об ошибке. Она говорит о том, что возникла проблема при работе с кучей, то есть с динамической памятью.
Для того, чтобы избежать такой проблемы мы можем использовать NULL .

3. NULL и nullptr
В таких языках программирования как Java или C#, NULL является отдельным типом данных и там ситуация несколько иная. В случае С++ мы имеем дело с NULL и nullptr .
nullptr — это более новая разработка, добавленная в С++ 11, и она уже работает аналогично тому как это реализовано в Java или C#. nullptr это отдельный тип данных, с которым компилятор ничего спутать не может. Что же касается NULL , то это просто эквивалент записи 0 (ноль).
Если мы напишем pa = 0 , то это значит, что наш указатель pa теперь не будет хранить адрес в памяти, а будет нулевым указателем, указывать на ноль. Вместо pa = 0 , мы можем записать pa = NULL — эти записи абсолютно равнозначны. Все дело в том, что NULL — это просто макрос.
Если мы наведем на него мышку, поставим курсор и нажимаем f12 , то увидим #define NULL 0 .

Строка pa = NULL говорит указателю, который до момента выполнения данной строки (где мы уже почистили память) указывает на определенный адрес оперативной памяти, чтобы он этот адрес забыл, чтобы мы к этому адресу впоследствии случайно не обратились. После того как мы присвоили NULL , у нас одни нули, т.е. нулевой указатель.
Если после такой операции мы попробуем еще раз сделать delete pa , то у нас все пройдет без проблем. Оператор delete посмотрит на то, что указатель указывает на NULL и не будет пытаться там что-то очистить, поэтому ошибку не получим. Теперь также мы явно можем проверять наш указатель на NULL , то есть на то, содержит ли он какой-то адрес или нет.
Если сейчас попробовать обратиться через cout , то в консоль будет выведен наш адрес — одни нули.

Добавим проверку if pa != 0 или if pa != NULL с возможностью выводить наш адрес указателя. В данном случае адрес не вывелся, поскольку указатель указывает на NULL . А раз он указывает на NULL , то он в принципе ничего не может хранить.
Таким образом мы перестраховываемся от того, чтобы получить или случайно записать некорректные данные.
Если мы уберем запись pa = NULL , то не сможем знать, куда указывает указатель, мы не можем перебрать все возможные адреса и знать что там лежит. Поэтому мы получим вывод нашего адреса и ошибку.
4. Тип данных nullptr
Возникает вопрос — для чего нужен отдельный тип данных nullptr? В принципе, мы можем использовать и NULL . В данном случае работать это будет точно так же.
Это уже не просто макрос и не просто нолик, а целочисленный тип Int . Это уже — отдельный тип данных. Мы его присваиваем и, на первый взгляд, разницы никакой нет. Однако для компилятора разница есть, он никогда не перепутает указатель nullptr с целочисленным типом данных.
К примеру, если у вас будет какая-то функция, она будет перегружена для типа Int и для указателя. И вы захотите передать в вашу функцию указатель с целочисленным нулем pa = 0 :
Ваша функция принимает либо указательную Int , либо просто целочисленный тип Int . В таком случае у компилятора могут быть проблемы какую функцию вызвать — с реализацией получается неоднозначность.
В С++ 11 nullptr это отдельный тип данных и компилятор никогда не перепутает его с обычным int . Поэтому в случаях когда вы будете работать с указателями, рекомендуется использовать именно его.
Если вы встретите где-то старый код, вы можете увидеть запись с присвоением нуля pa = NULL; (pa = 0;) . Теперь вы будете знать, что это такое и какие могут быть проблемы. Справедливости ради нужно сказать, что на самом деле проблемы возникают редко, но чтобы исключить их вообще, лучше использовать nullptr . Это хоть и редкий тип проблем, но очень коварный и трудно вычислимый.
Также стоит обратить внимание на еще один тип ошибок. Если вам нужно очистить динамическую память, в которой выделено место под ваши данные, то обязательно сначала нужно вызвать delete , ну, а затем, если нужно затереть адрес — присваивать нашему указателю nullptr .
Если вы сделаете наоборот, то есть сначала присвоите указателю nullptr , а затем присвоите указателю delete , то такое ваше действие приведет к утечке памяти.
Представьте себе, что мы имеем дело с программой, где указатель ссылается на определенные данные в оперативной памяти.


Если мы вызываем сначала delete , а затем присваиваем NULL , то сначала убиваются данные, а затем теряется и адрес, который хранил указатель. Однако, если вы сначала используете nullptr , тогда вы просто убираете адрес, но данные никуда не деваются, они так и остаются висеть в оперативной памяти.
Получается, что после того, как вы присвоили nullptr , но не удалили данные, вы уже никаким образом к ним не достучитесь — они останутся там висеть до тех пор, пока выполняется ваша программа.

Если в результате таких утечек памяти ваша программа упадет (если подобных утечек будет много), либо просто аварийно завершится, операционная система сама почистит память. Поэтому всегда нужно сначала удалять данные из динамической памяти, а затем присваивать nullptr , но ни коем случае не наоборот!
Заключение
Надеемся, что наш материал поможет вам избежать частых проблем при работе с памятью в C++. В заключение рекомендуем посмотреть видео, в котором рассказывается про указатели в С++
What is the use case and the difference between "nullptr" and "NULL"
Hi guys I just watch a tutorial about MOVE CONSTRUCTOR (better than deep copy ) and I don’t really understand the concepts I’m a beginner, not a pro so I need your help to understand let’s start: first here is the code from the tutorial :
OK HERE IS THE THING the instructor says "we steal the data and null the pointer so my question waht happend when we assighn our pointer to nullptre is it equal to zero or cant we reach it any more or what . "
2 Answers 2
NULL is more or less a left-over from simpler days, when C++ was much closer to its ancestor C. Since C++ was first standardized (and even before) it was recommended to use 0 for null-pointers. C++11 added nullptr which is a drop-in replacement for both 0 and NULL .
However the type of nullptr is std::nullptr_t which can be useful for templates and function arguments (for overloading).
![]()
A really nice book I was recommended recently: Effective Modern C++ by Scott Meyers.
Neither 0 nor NULL has a pointer type.
nullptr ’s actual type is std::nullptr_t
It can also improve code clarity, especially when auto variables are involved.
For example, suppose you encounter this in a code base:
If you don’t happen to know (or can’t easily find out) what findRecord returns, it may not be clear whether result is a pointer type or an integral type. After all, 0 (what result is tested against) could go either way. If you see the following, on the other hand,
there’s no ambiguity: result must be a pointer type.
nullptr shines especially brightly when templates enter the picture. Suppose you have some functions that should be called only when the appropriate mutex has been locked. Each function takes a different kind of pointer:
Calling code that wants to pass null pointers could look like this:
The failure to use nullptr in the first two calls in this code is sad, but the code works, and that counts for something. However, the repeated pattern in the calling code—lock mutex, call function, unlock mutex—is more than sad. It’s disturbing. This kind of source code duplication is one of the things that templates are designed to avoid, so let’s templatize the pattern:
If the return type of this function auto … -> decltype(func(ptr) has you scratching your head, you’ll see that in C++14, the return type could be reduced to a simple decltype(auto) :
Given the lockAndCall template (either version), callers can write code like this:
Difference between NULL and nullptr in C++
In this tutorial, we are going to learn about what is the difference between NULL & nullptr in C++.
NULL in C++
- “NULL” in C++ by default has the value zero (0) OR we can say that, “NULL” is a macro that yields to a zero pointer i.e. no address for that variable.
- In C-language, “NULL” is an old macro that is inherited in C++.
Let’s look at the example below that will help you to understand more clearly,