Why Are Null Pointers Defined Differently in C and C++

Why are NULL pointers defined differently in C and C++?

Back in C++03, a null pointer was defined by the ISO specification (§4.10/1) as

A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero.

This is why in C++ you can write

int* ptr = 0;

In C, this rule is similar, but is a bit different (§6.3.2.3/3):

An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant.55) If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal
to a pointer to any object or function.

Consequently, both

int* ptr = 0;

and

int* ptr = (void *)0

are legal. However, my guess is that the void* cast is here so that statements like

int x = NULL;

produce a compiler warning on most systems. In C++, this wouldn't be legal because you can't implicitly convert a void* to another pointer type implicitly without a cast. For example, this is illegal:

int* ptr = (void*)0; // Legal C, illegal C++

However, this leads to issues because the code

int x = NULL;

is legal C++. Because of this and the ensuing confusion (and another case, shown later), since C++11, there is a keyword nullptr representing a null pointer:

int* ptr = nullptr;

This doesn't have any of the above problems.

The other advantage of nullptr over 0 is that it plays better with the C++ type system. For example, suppose I have these two functions:

void DoSomething(int x);
void DoSomething(char* x);

If I call

DoSomething(NULL);

It's equivalent to

DoSomething(0);

which calls DoSomething(int) instead of the expected DoSomething(char*). However, with nullptr, I could write

DoSomething(nullptr);

And it will call the DoSomething(char*) function as expected.

Similarly, suppose that I have a vector<Object*> and want to set each element to be a null pointer. Using the std::fill algorithm, I might try writing

std::fill(v.begin(), v.end(), NULL);

However, this doesn't compile, because the template system treats NULL as an int and not a pointer. To fix this, I would have to write

std::fill(v.begin(), v.end(), (Object*)NULL);

This is ugly and somewhat defeats the purpose of the template system. To fix this, I can use nullptr:

std::fill(v.begin(), v.end(), nullptr);

And since nullptr is known to have a type corresponding to a null pointer (specifically, std::nullptr_t), this will compile correctly.

Hope this helps!

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

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.

Do all null pointers point to the same block of memory?

Let’s start with the definition of the NULL pointer.

6.3.2.3 Pointers
...

3     An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.66) If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

4     Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.

66) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.19.
C 2011 Online Draft

So, a null pointer does not necessarily mean address 0, it just means “whatever value the underlying system uses to indicate an invalid address” - think of it as a well-defined “nowhere”. It could be 0x00000000, it could be 0xFFFFFFFF, it could be 0xDEADBEEF, or anything else.

As far as your source code is concerned, NULL is always zero-valued, so you can test for NULL pointers like

int *ptr = NULL;
...
if ( ptr )
// ptr is not NULL, do something with it
else
// ptr is NULL

Two null pointers will evaluate to the same value, modulo any differences in type representation (different pointer types may have different size and alignment requirements).

Are these null pointers, or are they pointers to address 0?

p1 and p2 are null pointers; p3 is implementation defined,
and may be something else. (A comma operator cannot be part of
a constant expression. And the mapping of a non-constant
integral value 0 to a pointer is implementation defined.) C is
identical to C++ here.

p8 and p9 are both null pointers in C++, but not in C.

With regards to your comment on static_zero_2, there is no
requirement in either language that a literal zero be present,
anywhere. g++ defines NULL as the compiler built-in __null,
for example, and you can use (1 - 1), or '\0', or any other
constant expression evaluating to 0.

Is NULL always zero in C?

I'm assuming you mean the null pointer. It is guaranteed to compare equal to 0.1 But it doesn't have to be represented with all-zero bits.2

See also the comp.lang.c FAQ on null pointers.


  1. See C99, 6.3.2.3.
  2. There's no explicit claim; but see the footnote for C99, 7.20.3 (thanks to @birryree in the comments).

NULL vs nullptr (Why was it replaced?)

nullptr has type std::nullptr_t. It's implicitly convertible to any pointer type. Thus, it'll match std::nullptr_t or pointer types in overload resolution, but not other types such as int.

0 (aka. C's NULL bridged over into C++) could cause ambiguity in overloaded function resolution, among other things:

f(int);
f(foo *);

(Thanks to Caleth pointing this out in the comments.)

how to distinguish between 0 and NULL in C

There is a confusion between testing if the pointer to struct node passed to the function is a null pointer and the value of the integer data member of the nodes in the list it points to.

NULL is used in C as a macro to express a null pointer constant. It is used to test for null pointers. 0 is also a null pointer constant in a pointer context. Unlike in SQL, there is no concept of a null value representing no value in C for numeric values.

Here is a modified version of your code:

void ecrire(struct node *p) {
if (p == NULL) { // if the node pointer is NULL it means list is empty
printf("no items!\n");
return;
}
struct node *temp = p;
while (temp != NULL) {
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}

How is NULL different from 0 in C?

To the compiler, NULL is indistinguishable from 0 when used as a pointer. The standard actually defines 0 to be a null pointer value, and the standard library just introduces a convenient macro NULL defined to a null pointer value (usually 0 or ((void*)0)), so that you can use it in code for better readability and expressing intent. But there's nothing special about NULL itself; it's the 0 that is relevant.



Related Topics



Leave a reply



Submit