Search

8.7 — Null pointers

As you learned in the last lesson (8.6 -- Pointers), pointers are variables that hold an address. This address can be dereferenced using the dereference operator (*) to get the value at that address.

Pointers can be initialized to hold the address of another variable:

An uninitialized pointer is called a wild pointer, and will contain a garbage address. Dereferencing a wild pointer will result in undefined behavior.

A pointer that is pointing to object that is no longer valid (e.g. because the object has been destroyed) is called a dangling pointer. Dereferencing a dangling pointer will also result in undefined behavior.

Null pointers

Besides a memory address, there is one additional value that a pointer can hold: a null value. A null value (often shortened to null) is a special value that means the pointer is not pointing at anything. A pointer holding a null value is called a null pointer.

The easiest way to create a null pointer is to use value initialization to zero initialize the pointer:

Rule

Zero initialize your pointers (to be null pointers) if you are not initializing them with the address of a valid object.

The nullptr keyword

C++11 introduced a new keyword named nullptr. Much like true and false are keywords that represent Boolean literal values, nullptr is a keyword that represents a null pointer value.

Although we haven’t discussed pointers as function parameters yet, nullptr should also be preferred when passing a null pointer literal.

Best practice

Use nullptr when you need a null pointer literal for initialization, assignment, or passing to a function.

Pointers and Boolean conversions

If you recall from lessons of yore, integer values can be converted into to Boolean values: an integer value of 0 converts to Boolean value false, and any other integer value converts to Boolean value true.

Similarly, pointers can also be converted to Boolean values: a null pointer converts to Boolean value false, and a non-null pointer converts to Boolean value true.

Therefore, we can use a conditional (or the conditional operator) to test whether a pointer is a null pointer or not:

The above program prints:

ptr is non-null
nullPtr is null

Warning

Conditionals can only be used to differentiate null pointers from non-null pointers. There is no convenient way to determine whether a non-null pointer is pointing to a valid or invalid object.

Therefore, care needs to be taken to ensure that your pointers always either have the address of a valid object or are set to null (when not pointing to a valid object).

Dereferencing null pointers

Much like dereferencing a wild or dangling pointer leads to undefined behavior, dereferencing a null pointer also leads to undefined behavior. In most cases, it will crash your application.

The following program illustrates this, and will probably crash or terminate abnormally when you run it (go ahead, try it, you won’t harm your machine):

Conceptually, this makes sense. Dereferencing a pointer means “go to the address the pointer is pointing at and access the value there”. A null pointer doesn’t have an address. So when you try to access the value at that address, what should it do?

Accidentally dereferencing null or garbage pointers is one of the most common mistakes the C++ programmers make, and is probably the most common reason that C++ programs crash in practice.

Warning

Care needs to be taken to ensure pointers are non-null before dereferencing them or undefined behavior will result.

Legacy null pointer literals: 0 and NULL

In older code, you may see two other values used instead of nullptr.

The first is the literal 0. In the context of a pointer, the literal 0 is specially defined to mean a null pointer, and is the only time you can assign an integer literal to a pointer.

As an aside...

On modern architectures, the address 0 is typically used to represent a null pointer. However, this is not guaranteed by the C++ standard, and some architectures use other values. The literal 0, when used in the context of a null pointer, will be translated into whatever address the architecture uses to represent a null pointer.

Additionally, there is a preprocessor macro named NULL (defined in the <cstddef> header). This macro is inherited from C, where it is commonly used to indicate a null pointer.

Both 0 and NULL should be avoided in modern C++ (use nullptr instead).

For advanced readers

In this subsection, we’ll explain why using 0 or NULL is no longer preferred.

Note that the value of 0 isn’t a pointer value (it’s an integer literal), so assigning 0 (or NULL in some cases) to a pointer to denote that the pointer is a null pointer is a little inconsistent. In rare cases, when used as a literal argument, it can even cause problems because the compiler can’t tell whether we mean a null pointer value or the integer value 0.

We haven’t talked about this yet, but it turns out that you can define multiple functions with the same name so long as they take parameters of different types. The compiler can tell which one you want by the arguments passed in.

When using integer values, this can cause problems:

On the author’s machine, this prints:

print(int*): non-null
print(int): 0
print(int): 0
print(int*): null

Note that when passing integer value 0 as a parameter, C++ prefers print(int) over print(int*). This can lead to unexpected results.

In the likely case where NULL is defined as value 0, print(NULL) will call print(int), not print(int*) like you might expect for a null pointer literal.

std::nullptr_t

One interesting question: if nullptr can be differentiated from integer value 0, it must have a different type. What type is nullptr? The answer is that nullptr has type std::nullptr_t (defined in header <cstddef>). std::nullptr_t can only hold one value: nullptr! While this may seem kind of silly, it’s useful in one situation. If we want to write a function that accepts only a nullptr argument, what type do we make the parameter? The answer is std::nullptr_t.

You probably won’t ever need to use this, but it’s good to know, just in case.

Favor references over pointers whenever possible

Pointers are more flexible than references, as they can be changed to point at other addresses, or set to point at nothing. This means null pointers are common, and dangling pointers are easy to inadvertently create. Failure to guard against dereferencing such pointers will result in application crashes.

References are safer, and should be favored unless the additional flexibility provided by pointers is required.

Best practice

Favor references over pointers unless the additional flexibility provided by pointers is needed.


8.8 -- Pointers and const
Index
8.6 -- Pointers

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">