• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Servant of the Lord

NULL vs 0


44 posts in this topic

[quote name='Hodgman' timestamp='1355549999' post='5010854']
The same problem comes up in other situations BTW. As well as the type of literal "0" being ambiguous, there's many similar situations that a C++ programmer should be aware of. e.g. what's the type of the literal "1" below, and does the assertion fail?
[code]unsigned long long mask = 1<<48;
assert( mask == 0x1000000000000 );[/code]
[/quote]

I'll bite. The first line is undefined behavior on your average system where [font=courier new,courier,monospace]int[/font] is 32 bits, seeing as [font=courier new,courier,monospace]1 << 48[/font] is interpreted and evaluated as an [font=courier new,courier,monospace]int[/font], and shifting by more than 31 bits is undefined behavior on a 32-bit data type. The type of [font=courier new,courier,monospace]0x1000000000000[/font] will be [font=courier new,courier,monospace]long long[/font] (again, assuming your average system), but the comparison will be meaningless because of the previous undefined behavior (and as L. Spiro points out, [font=courier new,courier,monospace]mask[/font] will likely be zero).

And for the thread: I always used [font=courier new,courier,monospace]NULL[/font] because I found it to be a bit more self documenting than 0, but I also hated the fact that it was a macro and not built into the language's syntax. So I guess my official answer is: I always used [font=courier new,courier,monospace]NULL[/font], but I also always hated it.
1

Share this post


Link to post
Share on other sites
Although NULL IS ugly, it cannot be replaced with 0 (like others have explained). But you can always "rename" stuff in C/C++, so while it has been long since I last came across a compiler without support for nullptr, I would just redefine NULL as nullptr since I would probably also build the source on C++0x/C++11 complaint compilers, Of course, you loose the type of nullptr when compiling for pre-C++0x (std::nullptr_t) but with a little care (only load/store and compare with pointers), I cannot see why it would cause trouble.
[CODE]
#ifdef THIS_COMPILERS_VERSION_IS_WAY_TOO_OLD_FOR_MY_TASTE
#define nullptr NULL
#endif
[/CODE]

EDIT: An enhanced version might look like this, since it does not pollute the global namespace and even gives nullptr a type:
[CODE]
namespace SuperLibrary
{
#ifdef THIS_COMPILERS_VERSION_IS_WAY_TOO_OLD_FOR_MY_TASTE
const void *nullptr = (unsigned)NULL;
#endif
...
}
[/CODE] Edited by nife87
0

Share this post


Link to post
Share on other sites
[quote name='nife87' timestamp='1355564260' post='5010887']Although NULL IS ugly, it cannot be replaced with 0[/quote]What? [font=courier new,courier,monospace]NULL[/font] [b]is[/b] [font=courier new,courier,monospace]#define[/font]d as [font=courier new,courier,monospace]0[/font], meaning there's absolutely no difference except style/personal preference, and Bjarne "C++" Stroustrup was quoted earlier as preferring 0 ([i]or nullptr in C++11 where available[/i]) over the NULL macro. Edited by Hodgman
1

Share this post


Link to post
Share on other sites
[quote name='L. Spiro' timestamp='1355552518' post='5010858']
#2: 0 is the only literal constant that is defined by the standard to be implicitly castable to a pointer of any kind,
[/quote]
No, any integer constant expression that evaluates to zero is a valid null pointer constant. This includes 0 but also includes (1-1), (0xful >> 10), and '\0'. The standard specifically lists both 0 and 0L as two legal options for NULL. The latter is used by some compilers where an int is not the same size as a pointer, but a long is. This can give narrowing conversion warnings when trying to assign a NULL to a regular int. See the previously mentioned sections 8.1 in C++98/C++03 and 8.2 in C++11, as well as sections 4.10 and 5.19 (same number for all three).

[quote name='nife87' timestamp='1355564260' post='5010887']
I would just redefine NULL as nullptr since I would probably also build the source on C++0x/C++11 complaint compilers, Of course, you loose the type of nullptr when compiling for pre-C++0x (std::nullptr_t) but with a little care (only load/store and compare with pointers), I cannot see why it would cause trouble.
[/quote]
Again, as previously mentioned, NULL has an integer type which does not play happily with things like function overloads and template type deduction. If you have an overloaded function [tt]void foo(int)[/tt] and [tt]void foo(void *)[/tt], [tt]foo(NULL)[/tt] calls the first overload but [tt]foo(nullptr)[/tt] calls the second overload. Similarly if you have a [tt]vector<int *>[/tt], [tt]std::fill(vec.begin(), vec.end(), NULL)[/tt] will fail to compile but [tt]std::fill(vec.begin(), vec.end(), nullptr)[/tt] should.

[quote name='nife87' timestamp='1355564260' post='5010887']
const void *nullptr = (unsigned)NULL;
[/quote]
This is not a valid replacement for the C++11 nullptr. First, as previously mentioned, a void pointer cannot be assigned to other pointer types without an explicit cast. Basic usage such as [tt]int * a = nullptr;[/tt] would fail. Second, this isn't a constant expression. You've defined nullptr as a non-const pointer to const void. Presumably you wanted some variation with const on the right hand side of the * to make the pointer itself const. But also, even when dealing with void pointers you can still get into a situation where you need a cast because a [tt]const void *[/tt] is not assignable to a [tt]volatile void *[/tt] without a cast. Edited by SiCrane
1

Share this post


Link to post
Share on other sites
Stroustrup in the C++ FAQ:
"If you have to name the null pointer, call it nullptr; that's what it's called in C++11. Then, "nullptr" will be a keyword."

I think that makes sense. Use nullptr, and if you need pre-C++11 compatibility, define nullptr yourself conditionally like Servant of the Lord suggests, except make the definition as standard-conforming as possible (as shown by Martins Mozeiko) instead of just 0. If you needed both C++ and C compatibility, you'd have to likewise conditionally define nullptr as (void*)0 for C.

NULL is fine in pure C code, but there doesn't seem to be any reason to use it in C++. Edited by Stroppy Katamari
0

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1355570648' post='5010908']
[quote name='nife87' timestamp='1355564260' post='5010887']Although NULL IS ugly, it cannot be replaced with 0[/quote]What? [font=courier new,courier,monospace]NULL[/font] [b]is[/b] [font=courier new,courier,monospace]#define[/font]d as [font=courier new,courier,monospace]0[/font], meaning there's absolutely no difference except style/personal preference, and Bjarne "C++" Stroustrup was quoted earlier as preferring 0 ([i]or nullptr in C++11 where available[/i]) over the NULL macro.
[/quote]

Sorry. I was under the impression that NULL was commonly defined 0, but was not required to be defined as such. Well, that certainly makes life a bit easier.
1

Share this post


Link to post
Share on other sites
No, you were right. NULL is not required to be 0. In C++, NULL can be defined to be any integral constant expression that evaluates to zero. As I already mentioned, the C++ standard lists two possible conforming definitons: 0 and 0L, though those aren't the only possibilities.
1

Share this post


Link to post
Share on other sites
NULL is self documenting, but the documentation is misleading. It gives the impression that the compiler will know you are talking about a pointer, but that's not the case. As others have mentioned, if you have functions foo(int) and foo(int *), foo(NULL) will call the former. This is confusing as hell because NULL indicates one intent and the compiler does something else. IF you use 0, everyone knows what will happen. That seems more clear to me.
0

Share this post


Link to post
Share on other sites
NULL because reading code is harder than writing code, and using NULL makes it explicit that the variable being assigned to is a pointer type.

[quote name='Álvaro' timestamp='1355574263' post='5010923']
NULL is self documenting, but the documentation is misleading. It gives the impression that the compiler will know you are talking about a pointer, but that's not the case. As others have mentioned, if you have functions foo(int) and foo(int *), foo(NULL) will call the former. This is confusing as hell because NULL indicates one intent and the compiler does something else. IF you use 0, everyone knows what will happen. That seems more clear to me.
[/quote]

That doesn't actually resolve the initial problem you set out - even if you use 0 the wrong overload may still be called (assuming that foo (int *) was the intent). I'd go so far as to argue that using 0 instead of NULL is even more ambiguous in this case - when reading code, if you see a call to "foo (0)" how on earth are you going to know which overload the programmer intended? Using "foo (NULL)" at least gives you a chance - you know the programmer's intent straight away - they want the pointer version (the compiler may still get it wrong but at least you can now detect that and take appropriate remedial action).
0

Share this post


Link to post
Share on other sites
[quote name='mhagain' timestamp='1355577003' post='5010928']
NULL because reading code is harder than writing code, and using NULL makes it explicit that the variable being assigned to is a pointer type.

[quote name='Álvaro' timestamp='1355574263' post='5010923']
NULL is self documenting, but the documentation is misleading. It gives the impression that the compiler will know you are talking about a pointer, but that's not the case. As others have mentioned, if you have functions foo(int) and foo(int *), foo(NULL) will call the former. This is confusing as hell because NULL indicates one intent and the compiler does something else. IF you use 0, everyone knows what will happen. That seems more clear to me.
[/quote]

That doesn't actually resolve the initial problem you set out - even if you use 0 the wrong overload may still be called (assuming that foo (int *) was the intent). I'd go so far as to argue that using 0 instead of NULL is even more ambiguous in this case - when reading code, if you see a call to "foo (0)" how on earth are you going to know which overload the programmer intended? Using "foo (NULL)" at least gives you a chance - you know the programmer's intent straight away - they want the pointer version (the compiler may still get it wrong but at least you can now detect that and take appropriate remedial action).
[/quote]

That argument is really strange. Nobody expects the foo(0) will call the pointer version. 0 is an int. If the intent is to call the pointer version, you should write a cast, or it won't work, whether you use NULL or 0. Once you use the cast, the intent is clear.

[code]foo(0); // I want to call foo(int)
foo((int *)0); // I want to call foo(int*)[/code]
How is that not clear?
0

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1355549999' post='5010854']
The same problem comes up in other situations BTW. As well as the type of literal "0" being ambiguous, there's many similar situations that a C++ programmer should be aware of. e.g. what's the type of the literal "1" below, and does the assertion fail?
[code]unsigned long long mask = 1<<48;
assert( mask == 0x1000000000000 );[/code]
[/quote]

The literal "1" has type int. Probably "1ul" (if the compiler has 64-bit longs) or "1ull" was intended. [EDIT: Actually, the way the code is written, "1ull" was intended for sure.]

I am very careful to always use literals with the type I intend, since I was bitten by this one:
[code] double limit_price = order.is_market() ? 0 : order.limit_price();[/code]
At the time when I wrote this code Order::limit_price() used to return a double, and all was good. Then someone changed that to return a type Price, which provided an implicit conversion to double. The person doing that is a C++ expert, he tested the code, and it worked fine. Then we changed compilers from gcc-2.95.2 to gcc-3.4 (yeah, this is years ago), and then prices started to get truncated to integer values. This was not caught when testing the new compiler version (because someone messed up), and we ended up sending incorrect reports to a market regulator for several days. I spent about two weeks cleaning up the mess.

It turns out the C++ standard specifies that truncating to int is the correct behavior. Recent versions of g++ will warn about this, but it took them a long time to do so.

Since then, I write 0.0 if I intend it to be a double, 0.0f if I intend it to be a float, etc. I also don't like implicit conversions and I would much rather have an explicit Price::as_double() method that I need to call explicitly. Edited by Álvaro
0

Share this post


Link to post
Share on other sites
I spend a lot of time reading code, probably more than most. One of the few things that causes my brain to short-circuit for a while when doing this, is usage of 0 instead of NULL. For this reason, I have used NULL. Thankfully, so have most of the people whose code I have to read.

This is probably just learned preference, but thankfully we now do have somthing better, something that I have yearned for for a long time - a type safe null pointer!

Thanks Bjarne & co.
0

Share this post


Link to post
Share on other sites
[quote name='Álvaro' timestamp='1355580949' post='5010946']
0 is an int. If the intent is to call the pointer version, you should write a cast, or it won't work, whether you use NULL or 0. Once you use the cast, the intent is clear.

[code]foo(0); // I want to call foo(int)
foo((int *)0); // I want to call foo(int*)[/code]
How is that not clear?
[/quote]
Because 0 as a literal constant (or anything that results in a 0 constant) can be implicitly cast to any built-in type.
So 0 is not necessarily an [color=#000080]int[/color].

The problem with writing a cast explicitly every time you want to make your intent clear is that it leaves you with a whole lot of typing and thought. You have to cast to the correct type every time whereas just using NULL retains the same intent while keeping the coding process to a minimum.


L. Spiro
0

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1355570648' post='5010908']
[font=courier new,courier,monospace]NULL[/font] [b]is[/b] [font=courier new,courier,monospace]#define[/font]d as [font=courier new,courier,monospace]0[/font], meaning there's absolutely no difference except style/personal preference, and Bjarne "C++" Stroustrup was quoted earlier as preferring 0 ([i]or nullptr in C++11 where available[/i]) over the NULL macro.
[/quote]
As far as I understand, C++ does not require NULL to be zero.
It can be any integer beside 0.
I can not remember where I read, but on some embed system, NULL can be a value different than zero.
So he is correct.
0

Share this post


Link to post
Share on other sites
[quote name='wqking' timestamp='1355590219' post='5010980']
I can not remember where I read, but on some embed system, NULL can be a value different than zero.
[/quote]
What you might be thinking of is the actual representation on the machine being different from zero, which is allowed by the standard. It's also legal for some pointers to have a zero binary representation, but others have a non-zero binary representation. For instance, with MSVC most pointers use 0 for the actual representation of a null pointer, but pointers to members have the value 0xFFFFFFFF on 32-bit builds. However, no matter what value the machine actually sees, you can't use a non-zero integral constant to represent the null pointer.
0

Share this post


Link to post
Share on other sites
Macros are a fact of C &amp; C++ programming. e.g. You don't get compiler independent include guards, or a whole heap of other useful stuff like half of boost, without them. Claiming all macros are evil is like any other claim that all [i]XYZ[/i] are bad. There are always degrees of bad, and there are always exceptions.

NULL is like the least evil macro there is.
Use the tools you are provided with, in the way that they were intended to be used, that shows the most clear intentions.
The real problems are caused when you fight against the language / tools, and write unclear code. Edited by iMalc
2

Share this post


Link to post
Share on other sites
[quote name='Cornstalks' timestamp='1355594568' post='5010991']
Not in a standard complying C++ implementation. The C++ standard requires [font=courier new,courier,monospace]NULL[/font] to evaluate to zero (that is, [font=courier new,courier,monospace]NULL[/font] == 0 is always true). Embedded systems implementations sometimes break the rules of C++, so they're not exactly the best reference. If you don't believe me, read the [url="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3376.pdf"]C++ Standard[/url]:

[b]Section 18.2, paragraph 3[/b]: The macro NULL is an implementation-de?ned C++ null pointer constant in this International Standard
[b]Section 4.10, paragraph 1[/b]: A null pointer constant is an integral constant expression prvalue of integer type that evaluates to zero or a prvalue of type [font=courier new,courier,monospace]std::nullptr_t[/font]

Since [font=courier new,courier,monospace]NULL[/font] is required to be a "null pointer constant" and a "null pointer constant is an integral constant expression [...] that evaluates to zero" we can deduct that [font=courier new,courier,monospace]NULL[/font] evaluates to zero.
[/quote]
Thanks for pointing it out.
Knowing standard better is always good. :)
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0