21.2. Further Pointer Pathology with Heap Memory

[ fromfile: ptrpathology2.xml id: ptrpathologystorage ]

The result of applying delete to a pointer that holds the address of a valid object in the heap is to change the status of that heap memory from "in use" to "available." After delete has been applied to a pointer, the state of that pointer itself is undefined. The pointer may or may not still store the address of the deleted memory, so a second application of delete to the same pointer may cause runtime problems, possibly heap corruption.

In general the compiler cannot detect attempts to apply delete repeatedly to the same object, especially if that piece of memory (or a part thereof) has since been reallocated. To help avoid the undesirable consequences of a repeated delete it is good practice to assign 0 or NULL to a pointer immediately after it has been deleted.

If delete is applied to a null pointer, there is no error and no action.

Applying delete to a non-null pointer that was not returned by new produces undefined results. In general, the compiler cannot determine whether the pointer was returned by new so undefined runtime behavior can result. Bottom Line: It is the programmer's responsibility to use delete correctly.

One of the richest sources of runtime errors is the production of memory leaks. A memory leak is produced when a program causes memory to be allocated and then loses track of that memory so that it can neither be accessed nor deleted. An object that is not properly deleted will occupy memory until the process terminates.

Some programs (e.g., servers, operating systems) stay active for a long time. Suppose such a program contains a frequently executed routine that produces a memory leak each time it is run. The heap gradually becomes perforated with blocks of inaccessable, undeleted memory. At some point a routine that needs a substantial amount of contiguous dynamic memory may have its request denied. If the program is not prepared for an event like that, it will abort.

The operators new and delete give the C++ programmer increased power and increased responsibility.

Following is some sample code that illustrates a memory leak. After defining a couple of pointers, memory looks like Figure 21.1.

 int* ip = new int;        // allocate space for an int
 int* jp = new int(13);    // allocate and initialize
 cout << ip  << '\t'  <<  jp  << endl;
 

Figure 21.1.  Initial Values of Memory

Initial Values of Memory

 

After executing the following line of code, memory looks like Figure 21.2.

jp = new int(3); // reassign the pointer - MEMORY LEAK!!

Figure 21.2.  Memory After Leak

Memory After Leak

In Example 21.3, we delete the pointer jp twice.

Example 21.3. src/pointers/pathology/pathologydemo1.cpp

#include <iostream>
using namespace std;

int main() {
    int* jp = new int(13); 1
    cout << jp << '\t' << *jp << endl;
    delete jp;
    delete jp;             2
    jp = new int(3);       3
    cout << jp << '\t' << *jp << endl;
    jp = new int(10);      4
    cout << jp << '\t' << *jp << endl;
    int* kp = new int(17);
    cout << kp << '\t' << *kp << endl;
    return 0;
}
Output:

OOP> g++ pathologydemo1.cpp
OOP> ./a.out
0x8049e08       13
0x8049e08       3
0x8049e08       10
Segmentation fault
OOP>


1

Allocate and initialize.

2

Error: pointer already deleted.

3

Reassign the pointer, memory leak.

4

Reassign the pointer, memory leak.


The second deletion is a serious error but the compiler did not catch it. That error corrupted the heap, made any further memory allocation impossible, and made the behavior of the program beyond that point undefined. For example, notice that when we attempted to produce a memory leak by reassigning the pointer jp we did not get any new memory. When we attempted to introduce another pointer variable we got a segmentation fault. This is all undefined behavior and may be different on another platform or with another compiler.