Pointers allow us to work more directly with a computer's memory. Programmers generally don't know (and have no control over) where the compiler and the operating system place variables in memory, so we often think about pointers in general terms. Specifically, when we draw pictures of pointers, we often replace a specific address (a pointer variable's contents) with an arrow: The arrow points to the data whose address the pointer stores. Although we can't control a variable's placement in memory, we can find its address with one of the pointer operators and save it in a pointer variable. Once we have the variable's address, we can work with it indirectly through the pointer.
Although we can use many operators with pointers, six are common. C++ forms two of them as complete words: new
and delete
; one operator consists of two characters without any spaces: ->
, and the remaining three operators consist of a single character. Unfortunately, there are a limited number of characters on a keyboard, and all the symbols used to implement the single-character pointer operators have other overloaded meanings. The compiler distinguishes between the meanings based on context (i.e., where the symbols appear in a program). For example, the ampersand, &, forms the address of operator. Recall from the previous chapter that two adjacent ampersands form the logical-AND operator. Furthermore, the chapter 2 supplemental section introduces the single ampersand operator as the bitwise-AND operator. To make matters worse, besides its use as the multiplication operator, the asterisk, *, has two uses just within the context of pointers. First, it defines a pointer variable, and second, it serves as the dereference or the indirection operator (both name the same operation), which we take up in greater detail in the next section.
* | When used in a variable definition, defines a pointer variable |
* | When used in an expression, is the dereference operator (also known as the indirection operator) |
& | The address of operator |
The following figure demonstrates two operations with pointers. It illustrates defining a pointer variable, p, and how to find the address of an existing variable, i. The address-of-operator may be used with any variable regardless of its type. The demonstration uses the integer variable i for simplicity. Subsequent sections in this chapter describe the other operators.
(a) | int i; int* p; |
|
(b) | i = 123; p = &i; |
|
(c) |
int* p;
, int *p;
, or int * p;
.p
and &i
are assignment compatible because both sides of the assignment operator represent an address; that is, the assignment operation is possible because pointers are variables that store addresses.Many of the C++ operators introduced in previous chapters will "work" with pointers in the sense that they will compile and run without errors. However, not all operators produce meaningful or useful results when applied to pointers. If you recall how pointers "look," you can get a good sense of how or if most operators work with them.
T data; T* p = &data; ----------------- T* p = new T; |
|
(a) | (b) |
T* q = p; |
|
(c) | (d) |
sizeof(p) |
|
(e) | (f) |
We have, so far, only focused on the behavior of the pointer operators without considering why we might want to use them in a real-world program. We take this approach because pointers are a new concept to most of you, and I believe it is easier to introduce them without the distraction of a problem. However, it is challenging to appreciate the value of pointers without demonstrating their use. We will see some legitimate uses for pointers later in this chapter and throughout the text. But, before we see those examples, we must explore the indirection or dereference operator, the most challenging pointer operator to understand.