Suppose we've implemented a very simple linked class:
#include <iostream.h> class node { public: node(int i, node* n) { item_ = i; next_ = n; } int item() { return item_; } node* next() { return next_; } protected: int item_; node* next_; }; int main() { int i; node* current = 0; // build list node* head = new node(1, NULL); head = new node(2, head); // prepend new node head = new node(3, head); // prepend new node // print list current = head; for (i = 0; i <= 3; i++) // since list-size is now 3 { cout << "item " << i << " is " << current -> item() << endl; current = current -> next(); } // destroy list current = head; for (i = 0; i <= 3; i++) { current = head -> next(); delete head; head = current; } return 0; }It looks quite simple, but it still contains a bug:
list item 0 is 3 item 1 is 2 item 2 is 1 Memory faultFirst, we let GDB pinpoint the exact location of the memory fault:
GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.13 (sparc-sun-solaris2.3), Copyright 1994 Free Software Foundation, Inc... gdb list (gdb) run Starting program: /home/se/doc/debug/source/list item 0 is 3 item 1 is 2 item 2 is 1 Program received signal SIGSEGV, Segmentation fault. node::item (this=0x0) at list.cc:9 9 int item() { return item_; } (gdb)If the information provided by GDB is examined closely, it appears that the
this
-pointer is 0! This means a member function is called for a
non-existing object and this will indeed cause a memory fault. But how can
this happen? Use the command bt
to show the calling sequence that
resulted in the memory fault:
(gdb) bt #0 node::item (this=0x0) at list.cc:9 #1 0x800896e in main () at list.cc:32 (gdb) list list.cc:32 27 28 // print list 29 current = head; 30 for (i = 0; i <= 3; i++) // since list-size is now 3 31 { 32 cout << "item " << i << " is " << current -> item() << endl; 33 current = current -> next(); 34 } 35 36 // destroy list (gdb)It appears something goes wrong while printing all values. Since
current
is always pointing to the current object, it follows that
current
is at some point set to 0. But current
can only be 0
at the end of the list (when next_
equals 0). Let's see what happens
when the last item has been printed:
(gdb) br list.cc:32 Breakpoint 1 at 0x8008940: file list.cc, line 32. (gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/se/doc/debug/source/list Breakpoint 1, main () at list.cc:32 32 cout << "item " << i << " is " << current -> item() << endl; (gdb) cont Continuing. item 0 is 3 Breakpoint 1, main () at list.cc:32 32 cout << "item " << i << " is " << current -> item() << endl; (gdb) cont Continuing. item 1 is 2 Breakpoint 1, main () at list.cc:32 32 cout << "item " << i << " is " << current -> item() << endl; (gdb)So far, so good. This is the last item to be printed (since there are only 3 list-nodes). We now use tracing to see what happens next:
(gdb) n item 2 is 1 33 current = current -> next(); (gdb) n 34 } (gdb) print current $1 = (node *) 0x0 (gdb)As expected,
current
is now 0. But what happens now?
(gdb) n 32 cout << "item " << i << " is " << current -> item() << endl; (gdb)It appears the loop is not finished yet! How can that be?
(gdb) print i $2 = 3 (gdb)After some thinking, the problem is clear: the loop is executed four times instead of three times. The for-loop should be
for (i = 0; i < 3; i++) { ... }
and then it will work (the second
loop should of course be changed also).