Pointers are variables storing addresses, and addresses are just integers that programs use in a specific way. This observation implies that pointers are subject to the same arithmetic and relational operators as other integers. However, many pointer operations are meaningless despite compiling and running without errors. Some operations are beneficial, and two operators, ==
and !=
, and one constant, nullptr
, are essential for implementing correct and secure programs using pointers.
Applied to addresses, the range operators, <
, <=
, >
, and >=
produce meaningful results only when their operands lie within a contiguous memory block. However, programmers cannot control where the runtime system allocates data memory, so comparing the data's addresses with these operators is rarely meaningful. In contrast, programmers can use the equality and inequality operators, ==
and !=
, to compare the addresses of data allocated anywhere within a program's address space (i.e., on the stack or heap).
Every programming language using pointers includes a special pointer value indicating when a pointer does NOT point to anything. In C++, that value has three names. The preferred name is nullptr
, but the older NULL
and 0
(the single digit zero) still work and are still seen in older examples. Java programs have only one name available: null
.
It is possible, and very useful, to link data together using pointers (see Dynamic Data Structures for examples). With this organization, nullptr
indicates the end of the link. Control statements use tests incorporating the null pointer to manipulate these linked structures. In this context, the phrase "null pointer" is used in the conceptual or natural language sense. In a C++ program, a "null pointer" can be implemented with any of the three names stated previously: nullptr
(preferred), NULL
, or 0
.
Person* p0 = nullptr; Person* p1 = NULL; Person* p2 = 0; |
while (p0 != nullptr) .... while (p1 != NULL) .... while (p2 != 0) .... |
if (p0 == nullptr) .... if (p1 == NULL) .... if (p2 == 0) .... |
if (p0 != nullptr) .... if (p1 != NULL) .... if (p2 != 0) .... |
NULL
is a symbolic constant or macro implemented with a #define directive. Consequently, programs using it must #include an appropriate header file. Many of the common header files (e.g., <iostream>), and some less common ones, are "appropriate." Alternatively, nullptr
is a language-defined keyword recognized by the compiler, is easier to use, and does not require a specific header file.
Programming errors, informally but universally called bugs, involving pointers are notoriously difficult to find. They often lay dormant in large, complex programs for a considerable amount of time - often while the program is in full service. Pointer bugs cause frustration and embarrassment when the program eventually fails and are also a potential attack vector while the program is in service. Programmers must consider security throughout program development.
Pointers support a limited but essential set of arithmetic operations:
The operations do not change the values stored in the pointers but create an expression that points to a new location in memory.
The behavior of these operations, some of which are not completely intuitive, are most easily explored in terms of an abstract representation of main memory. Furthermore, the operations are only valid within a contiguous memory block without any "empty" space. An array is the most straightforward way to organize data this way. Adding an integer to or subtracting an integer from a pointer creates a new address that points to a different data item (i.e., a different array element). Taking the difference between two pointers calculates the number of data elements between the pointers. The following examples illustrate these operations.
The C++ compiler allows these simple arithmetic operations on pointers but not on references, marking the primary difference between them. Pointer arithmetic is very powerful and beneficial when used correctly and with discretion. However, it is also very error-prone, historically providing a gateway for many potent computer viruses. The potential harm of address arithmetic caused the Java language designers to abandon C++ and create a new language.