Stray or Dangling Pointers in C++

By: Kamini Viewed: 153215 times    

Stray or Dangling Pointers

One source of bugs that are nasty and difficult to find is stray pointers. A stray pointer is created when you call delete on a pointer--thereby freeing the memory that it points to--and later try to use that pointer again without reassigning it.

It is as though the Acme Mail Order company moved away, and you still pressed the programmed button on your phone. It is possible that nothing terrible happens--a telephone rings in a deserted warehouse. Perhaps the telephone number has been reassigned to a munitions factory, and your call detonates an explosive and blows up your whole city!

In short, be careful not to use a pointer after you have called delete on it. The pointer still points to the old area of memory, but the compiler is free to put other data there; using the pointer can cause your program to crash. Worse, your program might proceed merrily on its way and crash several minutes later. This is called a time bomb, and it is no fun. To be safe, after you delete a pointer, set it to null (0). This disarms the pointer.

 


NOTE: Stray pointers are often called wild pointers or dangling pointers.

Program below illustrates creating a stray pointer.


WARNING: This program intentionally creates a stray pointer. Do NOT run this program--it will crash, if you are lucky.

Creating a stray pointer.


1:     // 
2:     // Demonstrates a stray pointer
3:     typedef unsigned short int USHORT;
4:     #include <iostream.h>
5:
6:     int main()
7:     {
8:        USHORT * pInt = new USHORT;
9:        *pInt = 10;
10:       cout << "*pInt: " << *pInt << endl;
11:       delete pInt;
12:       pInt = 0;
13:       long * pLong = new long;
14:       *pLong = 90000;
15:       cout << "*pLong: " << *pLong << endl;
16:
17:       *pInt = 20;      // uh oh, this was deleted!
18:
19:       cout << "*pInt: " << *pInt  << endl;
20:       cout << "*pLong: " << *pLong  << endl;
21:       delete pLong;
22:    return 0;
23: }

Output: *pInt:   10
*pLong:  90000
*pInt:   20
*pLong:  65556
Null pointer assignment

(Your output may look different.)

Analysis: Line 8 declares pInt to be a pointer to USHORT, and pInt is pointed to newly allocated memory. Line 9 puts the value 10 in that memory, and line 10 prints its value. After the value is printed, delete is called on the pointer. pInt is now a stray, or dangling, pointer.
Line 13 declares a new pointer, pLong, which is pointed at the memory allocated by new.

Line 14 assigns the value 90000 to pLong, and line 15 prints its value.

Line 17 assigns the value 20 to the memory that pInt points to, but pInt no longer points anywhere that is valid. The memory that pInt points to was freed by the call to delete, so assigning a value to that memory is certain disaster.

Line 19 prints the value at pInt. Sure enough, it is 20. Line 20 prints 20, the value at pLong; it has suddenly been changed to 65556. Two questions arise:

1. How could pLong's value change, given that pLong wasn't touched?

2. Where did the 20 go when pInt was used in line 17?

As you might guess, these are related questions. When a value was placed at pInt in line 17, the compiler happily placed the value 20 at the memory location that pInt previously pointed to. However, because that memory was freed in line 11, the compiler was free to reassign it. When pLong was created in line 13, it was given pInt's old memory location. (On some computers this may not happen, depending on where in memory these values are stored.) When the value 20 was assigned to the location that pInt previously pointed to, it wrote over the value pointed to by pLong. This is called "stomping on a pointer." It is often the unfortunate outcome of using a stray pointer.
This is a particularly nasty bug, because the value that changed wasn't associated with the stray pointer. The change to the value at pLong was a side effect of the misuse of pInt. In a large program, this would be very difficult to track down.

Just for fun, here are the details of how 65,556 got into that memory address:

1. pInt was pointed at a particular memory location, and the value 10 was assigned.

2. delete was called on pInt, which told the compiler that it could put something else at that location. Then pLong was assigned the same memory location.

3. The value 90000 was assigned to *pLong. The particular computer used in this example stored the four-byte value of 90,000 (00 01 5F 90) in byte-swapped order. Therefore, it was stored as 5F 90 00 01.

4. pInt was assigned the value 20--or 00 14 in hexadecimal notation. Because pInt still pointed to the same address, the first two bytes of pLong were overwritten, leaving 00 14 00 01.

5. The value at pLong was printed, reversing the bytes back to their correct order of 00 01 00 14, which was translated into the DOS value of 65556.

DO use new to create objects on the free store. DO use delete to destroy objects on the free store and to return their memory. DON'T forget to balance all new statements with a delete statement. DON'T forget to assign null (0) to all pointers that you call delete on. DO check the value returned by new.

Most Viewed Articles (in C++ )

Latest Articles (in C++)

Comment on this tutorial