einpoklum
einpoklum

Reputation: 132320

Why is C++'s NULL typically an integer literal rather than a pointer like in C?

I've been writing C++ for many years, using nullptr for null pointers. I also know C, whence NULL originates, and remember that it's the constant for a null pointer, with type void *.

For reasons, I've had to use NULL in my C++ code for something. Well, imagine my surprise when during some template argument deduction the compiler tells me that my NULL is really a ... long. So, I double-checked:

#include <type_traits>
#include <cstddef>

static_assert(not std::is_same<decltype(NULL), long>::value, "NULL is long ???");

And indeed, the static assertion fails (with GCC and with Clang).

I checked on cppreference.com, and sure enough (C++11 wording):

The macro NULL is an implementation-defined null pointer constant, which may be an integer literal with value zero, or a prvalue of type std::nullptr_t.

Why does this make sense? In itself, and in light of the incompatibility of C?

Upvotes: 41

Views: 2931

Answers (4)

David Thornley
David Thornley

Reputation: 57076

C does not simply define NULL as (void *)0. It's also possible to define it as 0 or any constant expression that evaluates to it. Using #define NULL 0 works in either language.

Edit: this answer is very good and more detailed.

Upvotes: 10

Toby Speight
Toby Speight

Reputation: 30963

In both languages, NULL is an implementation-defined null pointer constant. C++ section 17.2.3, which refers to C 7.19 for the definition.

That means that in C, NULL can be defined as an integer type or as a void* type, and in C++ can be defined as an integer type; it could also be defined as a nullptr_t if I've interpreted the standard correctly. A hidden (non-normative) footnote says that (void*)0 can't be a valid definition, and that makes sense because C++ doesn't have C's implicit conversion from void* to other pointer types.

If you have a C compiler that defines it as ((void*)0) and a C++ compiler that defines it as 0L, they are both conformant. Both implementations satisfy the use cases (can assign from and compare with NULL).

C++ introduced nullptr primarily to give a typed value suitable for overloading, as it was considered surprising that passing NULL to a function could select an integer version.

Upvotes: 12

Sneftel
Sneftel

Reputation: 41542

My assumption has always been that implementations' decisions to keep NULL as 0 was a matter of backwards compatibility with C++98. I've seen a lot of code using NULL in non-pointer contexts, particularly as a (probably unintentional, or at least uninformed) substitute for a null terminator in an array. Since the best practice since '11 has been "don't use NULL at all", redefining NULL to nullptr would potentially break bad/old code, while doing very little to help good/new code.

Upvotes: 6

Nicol Bolas
Nicol Bolas

Reputation: 474436

In C, a void* can be implicitly converted to any T*. As such, making NULL a void* is entirely appropriate.

But that's profoundly dangerous. So C++ did away with such conversions, requiring you to do most pointer casts manually. But that would create source-incompatibility with C; a valid C program that used NULL the way C wanted would fail to compile in C++. It would also require a bunch of redundancy: T *pt = (T*)(NULL);, which would be irritating and pointless.

So C++ redefined the NULL macro to be the integer literal 0. In C, the literal 0 is also implicitly convertible to any pointer type and generates a null pointer value, behavior which C++ kept.

Now of course, using the literal 0 (or more accurately, an integer constant expression whose value is 0) for a null pointer constant was... not the best idea. Particularly in a language that allows overloading. So C++11 punted on using NULL entirely over a keyword that specifically means "null pointer constant" and nothing else.

Upvotes: 49

Related Questions