The Null Pointer Constant nullptr
The new null pointer nullptr cleans up in C++ with the ambiguity of 0 and the macro NULL.
The number 0
The issue with the literal 0 is that it can be the null pointer (void*)0 or the number 0. This is up to the context. I admit we are used to this oddity. But only almost.
Therefore, the small program with the number 0 should be confusing.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// null.cpp #include <iostream> #include <typeinfo> int main(){ std::cout << std::endl; int a= 0; int* b= 0; auto c= 0; std::cout << typeid(c).name() << std::endl; auto res= a+b+c; std::cout << "res: " << res << std::endl; std::cout << typeid(res).name() << std::endl; std::cout << std::endl; } |
The question is: What are the data type of variable c in line 12 and variable res in line 15?
The variable c is of type int, and the variable res is of type pointer to int: int*. Pretty simple, right? The expression a+b+c in line 15 is a pointer arithmetic.
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
The macro NULL
The issue with the null pointer NULL is that it implicitly converts to int. Not so nice.
According to en.cppreference.com the macro NULL is an implementation-defined null pointer constant. A possible implementation:
#define NULL 0 //since C++11 #define NULL nullptr
But that will not apply to my platform. Null seems to be of the type long int. I will refer to this point later. The usage of the macro NULL raises some questions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// nullMacro.cpp #include <iostream> #include <typeinfo> std::string overloadTest(int){ return "int"; } std::string overloadTest(long int){ return "long int"; } int main(){ std::cout << std::endl; int a= NULL; int* b= NULL; auto c= NULL; // std::cout << typeid(c).name() << std::endl; // std::cout << typeid(NULL).name() << std::endl; std::cout << "overloadTest(NULL)= " << overloadTest(NULL) << std::endl; std::cout << std::endl; } |
The compiler complains about the implicit conversion to int in line 19. That’s ok. But the warning in line 21 is confusing. The compiler automatically deduces the type of the variable c to long int. At the same time, it complains that the expression NULL must be converted. My observation is following the call overloadTest(NULL) in line 26. The compiler uses the version for the type long int (line 10). If the implementation uses NULL of type int, the compiler will choose overloadTest for the parameter type int (line 6). That is fine according to the C++ standard.
Now I want to know the current null pointer constant NULL type. Therefore, I comment on lines 22 and 23 of the program.
NULL seems for the compiler on one hand of type long int and the other hand a constant pointer. This behavior shows the compilation of the program nullMacro.cpp.
I learned my lesson. Don’t use the macro NULL.
But we have our rescue with the new null pointer constant nullptr.
The null pointer constant nullptr
The new null pointer nullptr cleans up in C++ with the ambiguity of 0 and the macro NULL. nullptr is and remains of type std::nullptr_t.
You can assign arbitrary pointers to a nullptr. The pointer becomes a null pointer and points to no data. You can not dereference a nullptr. The pointer of this type can on the one hand compared with all pointers and can, on the other hand, converted to all pointers. This also holds true for pointers to class members. But you can not compare and convert a nullptr to an integral type. There is one exception to this rule. You can implicitly compare and convert a bool value with a nullptr. Therefore, you can use a nullptr in a logical expression.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// nullptr.cpp #include <iostream> #include <string> std::string overloadTest(char*){ return "char*"; } std::string overloadTest(long int){ return "long int"; } int main(){ std::cout << std::endl; long int* pi = nullptr; // long int i= nullptr; // ERROR auto nullp= nullptr; // type std::nullptr_t bool b = nullptr; std::cout << std::boolalpha << "b: " << b << std::endl; auto val= 5; if ( nullptr < &val ){ std::cout << "nullptr < &val" << std::endl; } // calls char* std::cout << "overloadTest(nullptr)= " << overloadTest(nullptr)<< std::endl; std::cout << std::endl; } |
The nullptr can be used to initialize a pointer of type long int (line 18). But it can not be used to initialize a variable of type long int (line 18). The automatic type deduction in line 20 is quite interesting. nullptr becomes a value of type std::nullptr_t. The null pointer constant behaves like a boolean value initialized with false. You can observe that in lines 22 – 25. If the nullptr has to decide between a long int and a pointer, it will decide on a pointer (line 28).
Here is the output of the program.
The simple rule is: Use nullptr instead of 0 or NULL. Still not convinced? Here is my last and strongest point.
Generic code
The literal 0 and NULL show in generic code their true nature. Thanks to template argument deduction both literals are integral types in the function template. There is no hint that both literals were null pointer constants.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// generic.cpp #include <cstddef> #include <iostream> template<class P > void functionTemplate(P p){ int* a= p; } int main(){ int* a= 0; int* b= NULL; int* c= nullptr; functionTemplate(0); functionTemplate(NULL); functionTemplate(nullptr); } |
You can use 0 and NULL to initialize the int pointer in lines 12 and 13. But if you use the values 0 and NULL as function template arguments, the compiler will loudly complain. The compiler deduces 0 in the function template to type int and NULL to the type long int. But these observations will not hold for the nullptr. nullptr is in line 12 of type std::nullptr_t and nullptr is in line 8 of type std::nullptr_t.
What’s next?
In my last post, I presented many features in C++ to make your code safer. Which one? Have a look at high safety requirements on the overview page. The key idea of all these features is to use the compiler’s smartness. Therefore, we follow one of the critical principles of C++: Compile time errors are better than run time errors.
With the next posts, I switch the focus. My focus will change from the C++ features that are important for safety-critical features to the features that are important for performance reasons. I will have in the next post a deeper look into inline. Thanks to the keyword inline, the compiler can replace the function call with its function invocation. Therefore, the expensive call of the function becomes superfluous.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero | |
My special thanks to PVS-Studio | |
My special thanks to Tipi.build | |
My special thanks to Take Up Code | |
My special thanks to SHAVEDYAKS |
Modernes C++ GmbH
Modernes C++ Mentoring (English)
Rainer Grimm
Yalovastraße 20
72108 Rottenburg
Mail: schulung@ModernesCpp.de
Mentoring: www.ModernesCpp.org
Modernes C++ Mentoring,
Leave a Reply
Want to join the discussion?Feel free to contribute!