Do not mistake the pointing finger for the moon.
ZEN SAYING
A pointer is the memory address of a variable. Recall that the computer’s memory is divided into numbered memory locations (called bytes) and that variables are implemented as a sequence of adjacent memory locations. Recall also that sometimes the C++ system uses these memory addresses as names for the variables. If a variable is implemented as, say, three memory locations, then the address of the first of these memory locations is sometimes used as a name for that variable. For example, when the variable is used as a call-by-reference argument, it is this address, not the identifier name of the variable, that is passed to the calling function.
An address that is used to name a variable in this way (by giving the address in memory where the variable starts) is called a pointer because the address can be thought of as “pointing” to the variable. The address “points” to the variable because it identifies the variable by telling where the variable is, rather than telling what the variable’s name is. A variable that is, say, at location number 1007 can be pointed out by saying “it’s the variable over there at location 1007.”
You have already been using pointers in a number of situations. As we noted in the previous paragraph, when a variable is a call-by-reference argument in a function call, the function is given this argument variable in the form of a pointer to the variable. This is an important and powerful use for pointers, but it is done automatically for you by the C++ system. In this chapter, we show you how to write programs that manipulate pointers in any way you want, rather than relying on the system to manipulate the pointers for you.
A pointer can be stored in a variable. However, even though a pointer is a memory address and a memory address is a number, you cannot store a pointer in a variable of type int
or double
without type casting. A variable to hold a pointer must be declared to have a pointer type. For example, the following declares p to be a pointer variable that can hold one pointer that points to a variable of type double
:
double *p;
The variable p
can hold pointers to variables of type double
, but it cannot normally contain a pointer to a variable of some other type, such as int
or char
. Each variable type requires a different pointer type.
In general, to declare a variable that can hold pointers to other variables of a specific type, you declare the pointer variable just as you would declare an ordinary variable of that type, but you place an asterisk in front of the variable name. For example, the following declares the variables p1
and p2
so that they can hold pointers to variables of type int
; it also declares two ordinary variables, v1
and v2
, of type int
:
int *p1, *p2, v1, v2;
There must be an asterisk before each of the pointer variables. If you omit the second asterisk in the previous declaration, then p2
will not be a pointer variable; it will instead be an ordinary variable of type int
. The asterisk is the same symbol you have been using for multiplication, but in this context it has a totally different meaning.
When discussing pointers and pointer variables, we usually speak of pointing rather than of addresses. When a pointer variable, such as p1
, contains the address of a variable, such as v1
, the pointer variable is said to point to the variable v1
or to be a pointer to the variable v1
.
Pointer variables, like p1
and p2
declared earlier, can contain pointers to variables like v1
and v2
. You can use the reference operator &
to determine the address of a variable, and you can then assign that address to a pointer variable. For example, the following will set the variable p1
equal to a pointer that points to the variable v1
:
p1 = &v1;
You now have two ways to refer to v1
: You can call it v1
or you can call it “the variable pointed to by p1
.” In C++, the way that you say “the variable pointed to by p1
” is *p1
. This is the same asterisk that we used when we declared p1
, but now it has yet another meaning. When the asterisk is used in this way, it is often called the dereferencing operator, and the pointer variable is said to be dereferenced.
Putting these pieces together can produce some surprising results. Consider the following code:
v1 = 0;
p1 = &v1;
*p1 = 42;
cout << v1 << endl;
cout << *p1 << endl;
This code outputs the following to the screen:
42
42
As long as p1
contains a pointer that points to v1
, then v1
and *p1
refer to the same variable. So when you set *p1
equal to 42
, you are also setting v1
equal to 42
.
The symbol &
that is used to obtain the address of a variable is the same symbol that you use in function declarations to specify a call-by-reference parameter. This use is not a coincidence. Recall that a call-by-reference argument is implemented by giving the address of the argument to the calling function. So, these two uses of the symbol & are very much the same. However, the usages are slightly different and we will consider them to be two different (although very closely related) usages of the symbol &
.
You can assign the value of one pointer variable to another pointer variable. This copies an address from one pointer variable to another pointer variable. For example, if p1
is still pointing to v1
, then the following will set p2
so that it also points to v1
:
p2 = p1;
Provided we have not changed v1
’s value, the following also outputs a 42
to the screen:
cout << *p2;
Be sure that you do not confuse
p1 = p2;
and
*p1 = *p2;
When you add the asterisk, you are not dealing with the pointers p1
and p2
, but with the variables that the pointers are pointing to. This is illustrated in Display 9.1.
Since a pointer can be used to refer to a variable, your program can manipulate variables even if the variables have no identifiers to name them. The operator new
can be used to create variables that have no identifiers to serve as their names. These nameless variables are referred to via pointers. For example, the following creates a new variable of type int
and sets the pointer variable p1
equal to the address of this new variable (that is, p1
points to this new, nameless variable):
p1 = new int;
This new, nameless variable can be referred to as *p1
(that is, as the variable pointed to by p1
). You can do anything with this nameless variable that you can do with any other variable of type int
. For example, the following reads a value of type int
from the keyboard into this nameless variable, adds 7
to the value, then outputs this new value:
cin >> *p1;
*p1 = *p1 + 7;
cout << *p1;
The new
operator produces a new, nameless variable and returns a pointer that points to this new variable. You specify the type for this new variable by writing the type name after the new
operator. Variables that are created using the new
operator are called dynamic variables because they are created and destroyed while the program is running. The program in Display 9.2 demonstrates some simple operations on pointers and dynamic variables. Display 9.3 illustrates the working of the program in Display 9.2. In Display 9.3, variables are represented as boxes and the value of the variable is written inside the box. We have not shown the actual numeric addresses in the pointer variables. The actual numbers are not important. What is important is that the number is the address of some particular variable. So, rather than use the actual number of the address, we have merely indicated the address with an arrow that points to the variable with that address. For example, in illustration (b) in Display 9.3, p1
contains the address of a variable that has a question mark written in it.
1 //Program to demonstrate pointers and dynamic variables.
2 #include <iostream>
3 using namespace std;
4
5 int main( )
6 {
7 int *p1, *p2;
8
9 p1 = new int;
10 *p1 = 42;
11 p2 = p1;
12 cout<< "*p1 == " << *p1 << endl;
13 cout<< "*p2 == " << *p2 << endl;
14 *p2 = 53;
15 cout<< "*p1 == " << *p1 << endl;
16 cout<< "*p2 == " << *p2 << endl;
17 p1 = new int;
18 *p1 = 88;
19 cout<< "*p1 == " << *p1 << endl;
20 cout<< "*p2 == " << *p2 << endl;
21 cout<< "Hope you got the point of this example!\n";
22 return 0;
23 }
Sample Dialogue
*p1 == 42 *p2 == 42 *p1 == 53 *p2 == 53 *p1 == 88 *p2 == 53 Hope you got the point of this example!
Explain the concept of a pointer in C++.
What unfortunate misinterpretation can occur with the following declaration?
int* intPtr1, intPtr2;
Give at least two uses of the *
operator. State what the *
is doing, and name the use of the *
that you present.
What is the output produced by the following code?
int *p1, *p2;
p1 = new int;
p2 = new int;
*p1 = 10;
*p2 = 20;
cout << *p1 << " " << *p2 << endl;
p1 = p2;
cout << *p1 << " " << *p2 << endl;
*p1 = 30;
cout << *p1 << " " << *p2 << endl;
How would the output change if you were to replace
*p1 = 30;
with the following?
*p2 = 30;
What is the output produced by the following code?
int *p1, *p2;
p1 = new int;
p2 = new int;
*p1 = 10;
*p2 = 20;
cout << *p1 << " " << *p2 << endl;
*p1 = *p2; //This is different from Exercise 4
cout << *p1 << " " << *p2 << endl;
*p1 = 30;
cout << *p1 << " " << *p2 << endl;
A special area of memory, called the freestore, is reserved for dynamic variables. Any new dynamic variable created by a program consumes some of the memory in the freestore.2 If your program creates too many dynamic variables, it will consume all of the memory in the freestore. If this happens, any additional calls to new
will fail.
2 The freestore is also sometimes called the heap.
The size of the freestore varies by computer and implementation of C++. It is typically large, and a modest program is not likely to use all the memory in the freestore. However, even on modest programs it is a good practice to recycle any freestore memory that is no longer needed. If your program no longer needs a dynamic variable, the memory used by that dynamic variable can be recycled. The delete
operator eliminates a dynamic variable and returns the memory that the dynamic variable occupied to the freestore so that the memory can be reused. Suppose that p is a pointer variable that is pointing to a dynamic variable. The following will destroy the dynamic variable pointed to by p and return the memory used by the dynamic variable to the freestore:
delete p;
After this call to delete
, the value of p is undefined and p should be treated like an uninitialized variable.
delete
OperatorThe delete
operator eliminates a dynamic variable and returns the memory that the dynamic variable occupied to the freestore. The memory can then be reused to create new dynamic variables. For example, the following eliminates the dynamic variable pointed to by the pointer variable p:
delete p;
After a call to delete
, the value of the pointer variable, like p above, is undefined. (A slightly different version of delete
, discussed later in this chapter, is used when the dynamic variable is an array.)
When you apply delete
to a pointer variable, the dynamic variable it is pointing to is destroyed. At that point, the value of the pointer variable is undefined, which means that you do not know where it is pointing, nor what the value is where it is pointing. Moreover, if some other pointer variable was pointing to the dynamic variable that was destroyed, then this other pointer variable is also undefined. These undefined pointer variables are called dangling pointers. If p is a dangling pointer and your program applies the dereferencing operator *
to p (to produce the expression *p
), the result is unpredictable and usually disastrous. Before you apply the dereferencing operator *
to a pointer variable, you should be certain that the pointer variable points to some variable.
Variables created with the new
operator are called dynamic variables, because they are created and destroyed while the program is running. When compared with these dynamic variables, ordinary variables seem static, but the terminology used by C++ programmers is a bit more involved than that, and ordinary variables are not called static variables.
The ordinary variables we have been using in previous chapters are not really static. If a variable is local to a function, then the variable is created by the C++ system when the function is called and is destroyed when the function call is completed. Since the main part of a program is really just a function called main
, this is even true of the variables declared in the main part of your program. (Since the call to main
does not end until the program ends, the variables declared in main
are not destroyed until the program ends, but the mechanism for handling local variables is the same for main
as it is for any other function.) The ordinary variables that we have been using (that is, the variables declared within main
or within some other function definition) are called automatic variables (not to be confused with variables defined of type auto
), because their dynamic properties are controlled automatically for you; they are automatically created when the function in which they are declared is called and automatically destroyed when the function call ends. We will usually call these variables ordinary variables, but other books call them automatic variables.
There is one other category of variables, namely, global variables. Global variables are variables that are declared outside of any function definition (including being outside of main
). We discussed global variables briefly in Chapter 4. As it turns out, we have no need for global variables and have not used them.
You can define a pointer type name so that pointer variables can be declared like other variables without the need to place an asterisk in front of each pointer variable. For example, the following defines a type called IntPtr
, which is the type for pointer variables that contain pointers to int
variables:
typedef int* IntPtr;
Thus, the following two pointer variable declarations are equivalent:
IntPtr p;
and
int *p;
You can use typedef
to define an alias for any type name or definition. For example, the following defines the type name Kilometers
to mean the same thing as the type name double
:
typedef double Kilometers;
Once you have given this type definition, you can define a variable of type double
as follows:
Kilometers distance;
Renaming existing types this way can occasionally be useful. However, our main use of typedef
will be to define types for pointer variables.
There are two advantages to using defined pointer type names, such as IntPtr
defined earlier. First, it avoids the mistake of omitting an asterisk. Remember, if you intend p1
and p2
to be pointers, then the following is a mistake:
int *p1, p2;
Since the *
was omitted from the p2
, the variable p2
is just an ordinary int
variable, not a pointer variable. If you get confused and place the *
on the int
, the problem is the same but is more difficult to notice. C++ allows you to place the *
on the type name, such as int
, so that the following is legal:
int* p1, p2;
Although this line is legal, it is misleading. It looks like both p1
and p2
are pointer variables, but in fact only p1
is a pointer variable; p2
is an ordinary int
variable. As far as the C++ compiler is concerned, the *
that is attached to the identifier int
may as well be attached to the identifier p1
. One correct way to declare both p1
and p2
to be pointer variables is
int *p1, *p2;
An easier and less error-prone way to declare both p1
and p2
to be pointer variables is to use the defined type name IntPtr
as follows:
IntPtr p1, p2;
The second advantage of using a defined pointer type, such as IntPtr
, is seen when you define a function with a call-by-reference parameter for a pointer variable. Without the defined pointer type name, you would need to include both an *
and an &
in the function declaration for the function, and the details can get confusing. If you use a type name for the pointer type, then a call-by-reference parameter for a pointer type involves no complications. You define a call-by-reference parameter for a defined pointer type just like you define any other call-by-reference parameter. Here’s a sample:
void sample_function(IntPtr& pointer_variable);
You can assign a name to a type definition and then use the type name to declare variables. This is done with the keyword typedef
. These type definitions are normally placed outside of the body of the main part of your program (and outside the body of other functions).
We will use type definitions to define names for pointer types, as shown in the example below.
typedef Known_Type_Definition New_Type_Name;
typedef int* IntPtr;
The type name IntPtr
can then be used to declare pointers to dynamic variables of type int
, as in the following:
IntPtr pointer1, pointer2;
Suppose a dynamic variable were created as follows:
char *p;
p = new char;
Assuming that the value of the pointer variable p has not changed (so it still points to the same dynamic variable), how can you destroy this new dynamic variable and return the memory it uses to the freestore so that the memory can be reused to create new dynamic variables?
Write a definition for a type called NumberPtr
that will be the type for pointer variables that hold pointers to dynamic variables of type int
. Also, write a declaration for a pointer variable called my_point
that is of type NumberPtr
.
Describe the action of the new
operator. What does the operator new
return?