4.11. Pointers Summary

Programs partition their memory into functional groups, allocating memory for new data (i.e., variables) from two of them. Programs allocate memory for automatic variables on the stack and dynamic variables on the heap. The algorithms managing the stack and heap are different:

  C++ Code Abstract Representation
(a)
int	counter = 123;
int*	cntptr = &counter;
The picture depicts the variable counter as a rectangle with 123 inside, which is the content of the variable. counter also has an address that is 0x000000ab. The picture also depicts the variable cntptr as a rectangle, but its contents (i.e., the value written inside the rectangle), is 0x000000ab. cntptr also has an address: 0x000000af, which is four bytes beyond the address of counter.
(b)   An alternate way to view the relation between a pointer and the data to which it points: The picture represents cntptr as a rectangle, but in this illustration, an arrow points from cntptr to the rectangle representing counter.
(c)
cout << counter << endl;
An illustration showing that it takes a single memory access to print counter.
(d)
cout << *cntptr << endl;
An illustration showing that it takes two memory accesses to print counter indirectly through cntptr.
Automatic (stack) pointer variables and operators. Demonstrates the pointer-definition, address-of, and dereference operators with simple C++ statements and contrasts the statements with abstract representations of how the variables appear in main memory and react to the operators.
  1. Defines and initializes an integer variable named counter and a pointer variable named cntptr. The variable definitions allocate memory. Each variable has a name, contents, and an address. In this example, the address-of operator, &, forms an expression whose value is the address of its operand, counter. The second assignment operation saves cntptr's address in counter.
  2. An alternate view of (a) with an arrow from the pointer variable, cntptr, to the data variable, counter. The arrow replaces the arbitrary and unknown address in the first illustration.
  3. The computer goes to the address corresponding to counter, loads the value saved at that location, and prints it to the console, accessing memory once.
  4. The expression *cntptr demonstrates the indirection or dereference operator by printing the contents of counter indirectly through the pointer variable cntptr. The computer goes to the memory address corresponding to cntptr, retrieves the address saved there, goes to the retrieved address, loads the value saved there, and prints it to the console. Thus accessing a variable's content without using its name. Accessing the content of one variable indirectly through another variable takes two trips to memory, is called dereferencing, and can only be done with a pointer variable.
(a)
int* ip = new int;
The illustration represents the variable ip as a rectangle with an arrow leaving it and pointing to another rectangle representing the memory allocated on the heap.
(b)
int* ia = new int[100];
The picture represents variable ia as a rectangle with an arrow leaving it and pointing to a larger rectangle, divided into small squares, representing the memory allocated on the heap for the array; the small squares in the large rectangle represent the individual array elements.
(c)
Person* p = new Person;
The picture represents variable p as a rectangle with an arrow leaving it and pointing to a larger rectangle representing the memory allocated for an object on the heap; the large rectangle contains two rectangles that represent member variables or data maintained inside of the object.
(d)
cout << *ip << endl;
(e)
cout << ia[5] << endl;
(f)
p->name;
p->pay_taxes();
(g)
delete ip;
After the delete operation, ip still points to the memory originally allocated with new, but the picture now shows the rectangle representing that memory with a dashed line, suggesting the delete operation deallocated it.
(h)
delete[] ia;
After the delete operation, ia still points to the array memory, although delete has deallocated it. The picture suggests the array's deallocation by drawing it with dashed lines.
(i)
delete p;
The delete operator deallocates the object's heap memory, illustrated by drawing it with dashed lines, but leaves p pointing to the deallocated memory.
Allocating, using, and deallocating dynamic memory. The new operator allocates memory on the heap and returns its address; programs typically store the address in a pointer variable. Various pointer operators allow programmers to manipulate or use the memory. The memory remains allocated until the programmer explicitly deallocates it with the delete operator.
  1. Allocating memory for a single, small data type (an int in this example) on the heap is rare.
  2. Allocating memory for an array on the heap is common; the array size can be determined at runtime (i.e., the size can be a variable).
  3. Assuming Person names a class, the example demonstrates allocating memory for an object on the heap.
  4. Dereferencing a pointer pointing to memory on the heap; the dereference operation is the same for memory allocated on the stack (compare with Figure 1(d))
  5. The index operator, [], operates on an address. Programmers often use it with automatic arrays allocated on the stack. But they can also use it with pointers pointing to dynamic arrays allocated on the heap.
  6. The arrow operator, ->, selects the data and the functions of an object
  7. Deallocating and returning the memory for a single data item to the heap.
  8. Deallocating and returning the memory for an array to the heap; the brackets at the end of "delete" are necessary to call the destructor (introduced in Chapter 9) for each object in the array
  9. Deallocating and returning the memory for an object to the heap.
The delete operator, (g), (h), and (i), does not change the address saved in the pointer variable. So, the pointer still points to the deallocated memory following the deletion, but programs should not use or access that memory in any way.