C++

Pointers

To declare a pointer, write the type of the variable or object whose address will be stored in the pointer, followed by the pointer operator (*) and the name of the pointer. For example,

unsigned short int * pPointer = 0;

To assign or initialize a pointer, prepend the name of the variable whose address is being assigned with the address of operator (&). For example,

unsigned short int theVariable = 5;

unsigned short int * pPointer = & theVariable;

To dereference a pointer, prepend the pointer name with the dereference operator (*). For example,

unsigned short int theValue = *pPointer

Why Would You Use Pointers?

So far you’ve seen step-by-step details of assigning a variable’s address to a pointer. In practice, though, you would never do this. After all, why bother with a pointer when you already have a variable with access to that value? The only reason for this kind of pointer manipulation of an automatic variable is to demonstrate how pointers work. Now that you are comfortable with the syntax of pointers, you can put them to good use. Pointers are used, most often, for three tasks:

· Managing data on the free store.

· Accessing class member data and functions.

· Passing variables by reference to functions.

This rest of this unit focuses on managing data on the free store and accessing class member data and functions. Tomorrow you will learn about passing variables by reference.

The Stack and the Free Store

In the "Extra Credit" section following the discussion of functions in unit 5, five areas of memory are mentioned:

· Global name space

· The free store

· Registers

· Code space

· The stack

Local variables are on the stack, along with function parameters. Code is in code space, of course, and global variables are in global name space. The registers are used for internal housekeeping functions, such as keeping track of the top of the stack and the instruction pointer. Just about all remaining memory is given over to the free store, which is sometimes referred to as the heap.

The problem with local variables is that they don’t persist: When the function returns, the local variables are thrown away. Global variables solve that problem at the cost of unrestricted access throughout the program, which leads to the creation of code that is difficult to understand and maintain. Putting data in the free store solves both of these problems.

You can think of the free store as a massive section of memory in which thousands of sequentially numbered cubbyholes lie waiting for your data. You can’t label these cubbyholes, though, as you can with the stack. You must ask for the address of the cubbyhole that you reserve and then stash that address away in a pointer.

One way to think about this is with an analogy: A friend gives you the 800 number for Acme Mail Order. You go home and program your telephone with that number, and then you throw away the piece of paper with the number on it. If you push the button, a telephone rings somewhere, and Acme Mail Order answers. You don’t remember the number, and you don’t know where the other telephone is located, but the button gives you access to Acme Mail Order. Acme Mail Order is your data on the free store. You don’t know where it is, but you know how to get to it. You access it by using its address—in this case, the telephone number. You don’t have to know that number; you just have to put it into a pointer (the button). The pointer gives you access to your data without bothering you with the details.

The stack is cleaned automatically when a function returns. All the local variables go out of scope, and they are removed from the stack. The free store is not cleaned until your program ends, and it is your responsibility to free any memory that you’ve reserved when you are done with it.

The advantage to the free store is that the memory you reserve remains available until you explicitly free it. If you reserve memory on the free store while in a function, the memory is still available when the function returns.

The advantage of accessing memory in this way, rather than using global variables, is that only functions with access to the pointer have access to the data. This provides a tightly controlled interface to that data, and it eliminates the problem of one function changing that data in unexpected and unanticipated ways.

For this to work, you must be able to create a pointer to an area on the free store and to pass that pointer among functions. The following sections describe how to do this.

new

You allocate memory on the free store in C++ by using the new keyword. new is followed by the type of the object that you want to allocate so that the compiler knows how much memory is required. Therefore, new unsigned short int allocates two bytes in the free store, and new long allocates four.

The return value from new is a memory address. It must be assigned to a pointer. To create an unsigned short on the free store, you might write

unsigned short int * pPointer;

pPointer = new unsigned short int;

You can, of course, initialize the pointer at its creation with

unsigned short int * pPointer = new unsigned short int;

In either case, pPointer now points to an unsigned short int on the free store. You can use this like any other pointer to a variable and assign a value into that area of memory by writing

*pPointer = 72;

This means, "Put 72 at the value in pPointer," or "Assign the value 72 to the area on the free store to which pPointer points."

If new cannot create memory on the free store (memory is, after all, a limited resource) it returns the null pointer. You must check your pointer for null each time you request new memory.

WARNING: Each time you allocate memory using the new keyword, you must check to make sure the pointer is not null.

delete

When you are finished with your area of memory, you must call delete on the pointer. delete returns the memory to the free store. Remember that the pointer itself—as opposed to the memory to which it points—is a local variable. When the function in which it is declared returns, that pointer goes out of scope and is lost. The memory allocated with new is not freed automatically, however. That memory becomes unavailable—a situation called a memory leak. It’s called a memory leak because that memory can’t be recovered until the program ends. It is as though the memory has leaked out of your computer.

To restore the memory to the free store, you use the keyword delete. For example,

delete pPointer;

When you delete the pointer, what you are really doing is freeing up the memory whose address is stored in the pointer. You are saying, "Return to the free store the memory that this pointer points to." The pointer is still a pointer, and it can be reassigned. Listing 8.4 demonstrates allocating a variable on the heap, using that variable, and deleting it.

WARNING: When you call delete on a pointer, the memory it points to is freed. Calling delete on that pointer again will crash your program! When you delete a pointer, set it to zero (null). Calling delete on a null pointer is guaranteed to be safe. For example:

Animal *pDog = new Animal; delete pDog; //frees the memory

pDog = 0; //sets pointer to null //... delete pDog; //harmless

Listing 8.4. Allocating, using, and deleting pointers.

1: // Listing 8.4

2: // Allocating and deleting a pointer

3:

4: #include <iostream.h>

5: int main()

6: {

7: int localVariable = 5;

8: int * pLocal= &localVariable;

9: int * pHeap = new int;

10: if (pHeap == NULL)

11: {

12: cout << "Error! No memory for pHeap!!";

13: return 0;

14: }

15: *pHeap = 7;

16: cout << "localVariable: " << localVariable << "\n";

17: cout << "*pLocal: " << *pLocal << "\n";

18: cout << "*pHeap: " << *pHeap << "\n";

19: delete pHeap;

20: pHeap = new int;

21: if (pHeap == NULL)

22: {

23: cout << "Error! No memory for pHeap!!";

24: return 0;

25: }

26: *pHeap = 9;

27: cout << "*pHeap: " << *pHeap << "\n";

28: delete pHeap;

29: return 0;

30: }

Output:

localVariable: 5

*pLocal: 5

*pHeap: 7

*pHeap: 9

Analysis: Line 7 declares and initializes a local variable. Line 8 declares and initializes a pointer with the address of the local variable. Line 9 declares another pointer but initializes it with the result obtained from calling new int. This allocates space on the free store for an int. Line 10 verifies that memory was allocated and the pointer is valid (not null). If no memory can be allocated, the pointer is null and an error message is printed. To keep things simple, this error checking often won’t be reproduced in future programs, but you must include some sort of error checking in your own programs.

Line 15 assigns the value 7 to the newly allocated memory. Line 16 prints the value of the local variable, and line 17 prints the value pointed to by pLocal. As expected, these are the same. Line 19 prints the value pointed to by pHeap. It shows that the value assigned in line 15 is, in fact, accessible.

In line 19, the memory allocated in line 9 is returned to the free store by a call to delete. This frees the memory and disassociates the pointer from that memory. pHeap is now free to point to other memory. It is reassigned in lines 20 and 26, and line 27 prints the result. Line 28 restores that memory to the free store.

Although line 28 is redundant (the end of the program would have returned that memory) it is a good idea to free this memory explicitly. If the program changes or is extended, it will be beneficial that this step was already taken care of.

Back to Index