A Closer Look at the Warehouse Program

In this handout, we use gdb to do more exploration of how pointers work in the warehouse program. (Note: We are not using gdb for debugging here; we are just using it as a convenient way to print out some information on the program's actions.)

  1  Script started on Mon Apr 27 22:15:30 1992
  2  heather% gdb a.out
  3  GDB is free software and you are welcome to distribute copies of it
  4   under certain conditions; type "info copying" to see the conditions.
  5  There is absolutely no warranty for GDB; type "info warranty" for details.
  6  GDB 4.1, Copyright 1991 Free Software Foundation, Inc...
  7  (gdb) l 130
  8  125     
  9  126     GetData()  /* prompt user for the information, and put it in
 10  127                   the struct pointed to by CurrPtr */
 11  128     
 12  129     {  int ItmNm,TmStmp,CstmrID;  
 13  130     
 14  131        printf("enter item number, time stamp and customer i.d.\n");
 15  132        scanf("%d%d%d",&ItmNm,&TmStmp,&CstmrID);
 16  133     
 17  134        /* create new node */
 18  (gdb) 
 19  135        CurrPtr = (NodePtrType) calloc(1,sizeof(struct ListNode));  
 20  136     
 21  137        /* now fill it with the data */
 22  138        CurrPtr->ItemNum = ItmNm;
 23  139        CurrPtr->TimeStamp = TmStmp;
 24  140        CurrPtr->CustomerID = CstmrID;
 25  141     
 26  142        /* this node is not connected to anything yet */
 27  143        CurrPtr->Link = 0;  
 28  144     }
 29  (gdb) 
 30  145     
 31  146     
 32  147     Insert()  /* inserts the struct pointed to by CurrPtr into the
 33  148                  list */
 34  149     
 35  150     {  NodePtrType LeadingPtr,TrailingPtr;
 36  151     
 37  152        /* list currently empty? */
 38  153        if (ListHead == 0)  {
 39  154           ListHead = CurrPtr;
 40  (gdb) 
 41  155           return;
 42  156        }
 43  157        
 44  158        /* current time stamp earlier than list head? */
 45  159        if (CurrPtr->TimeStamp < ListHead->TimeStamp)  {
 46  160           CurrPtr->Link = ListHead;
 47  161           ListHead = CurrPtr;
 48  162           return;
 49  163        }
 50  164        
 51  (gdb) 
 52  165        LeadingPtr = ListHead->Link;  TrailingPtr = ListHead;
 53  166        while (1)  {
 54  167     
 55  168           /* reached end of list? */
 56  169           if (LeadingPtr == 0)  {
 57  170              TrailingPtr->Link = CurrPtr;
 58  171              break;
 59  172           }
 60  173     
 61  174           /* arrived at insertion point? */
 62  (gdb) 
 63  175           if (CurrPtr->TimeStamp < LeadingPtr->TimeStamp)  {
 64  176              TrailingPtr->Link = CurrPtr;
 65  177              CurrPtr->Link = LeadingPtr;
 66  178              return;
 67  179           }
 68  180           /* otherwise move to next node */
 69  181           else  {
 70  182              TrailingPtr = LeadingPtr;
 71  183              LeadingPtr = LeadingPtr->Link;
 72  184           }
 73  (gdb) 
 74  185        }
 75  186     }
 76  187     
 77  188     
 78  189     Cancel()  /* delete the node whose time stamp matches CancelTmStmp;
 79  190                  to keep the program simple so as to ease the teaching 
 80  191                  about pointers, it is assumed that there will be one, 
 81  192                  and only one, node which matches the given time stamp */
 82  193     
 83  194     {  NodePtrType LeadingPtr,TrailingPtr;
 84  (gdb) b 138
 85  Breakpoint 1 at 0x2544: file Warehouse.c, line 138.
 86  (gdb) ignore 1 2
 87  Will ignore next 2 crossings of breakpoint 1.
 88  (gdb) r
 89  Starting program: /1/home/matloff/Courses/40/Progs/a.out 
 90  read in a file?
 91  n
 92  enter command
 93  p
 94  enter item number, time stamp and customer i.d.
 95  234 44 1
 96  enter command
 97  p
 98  enter item number, time stamp and customer i.d.
 99  17 68 220
100  enter command
101  p
102  enter item number, time stamp and customer i.d.
103  4 56 18
104  
105  Breakpoint 1, GetData () at Warehouse.c:138
106  138        CurrPtr->ItemNum = ItmNm;
107  (gdb) p CurrPtr
108  $1 = (NodePtrType) 0x6710
109  (gdb) b 175
110  Breakpoint 2 at 0x2654: file Warehouse.c, line 175.
111  (gdb) c
112  Continuing.
113  
114  Breakpoint 2, Insert () at Warehouse.c:175
115  175           if (CurrPtr->TimeStamp < LeadingPtr->TimeStamp)  {
116  (gdb) define cld
117  Type commands for definition of "cld".
118  End with a line saying just "end".
119  call Display()
120  end
121  (gdb) disp LeadingPtr
122  1: LeadingPtr = (NodePtrType) 0x66f8
123  (gdb) disp TrailingPtr
124  2: TrailingPtr = (NodePtrType) 0x66e0
125  (gdb) cld
126  66e0 234 44 1 66f8
127  66f8 17 68 220 0
128  $2 = 17832
129  (gdb) n
130  176              TrailingPtr->Link = CurrPtr;
131  2: TrailingPtr = (NodePtrType) 0x66e0
132  1: LeadingPtr = (NodePtrType) 0x66f8
133  (gdb) n
134  177              CurrPtr->Link = LeadingPtr;
135  2: TrailingPtr = (NodePtrType) 0x66e0
136  1: LeadingPtr = (NodePtrType) 0x66f8
137  (gdb) cld
138  66e0 234 44 1 6710
139  6710 4 56 18 0
140  $3 = 17832
141  (gdb) p *LeadingPtr
142  $4 = {ItemNum = 17, TimeStamp = 68, CustomerID = 220, Link = 0x0}
143  (gdb) n
144  178              return;
145  2: TrailingPtr = (NodePtrType) 0x66e0
146  1: LeadingPtr = (NodePtrType) 0x66f8
147  (gdb) cld
148  66e0 234 44 1 6710
149  6710 4 56 18 66f8
150  66f8 17 68 220 0
151  $5 = 17832
152  (gdb) qy
153  heather% e
154  heather% 
155  script done on Mon Apr 27 22:21:20 1992

Note: Except when stated otherwise, all line numbers below refer to those of the script file, not those of the C source file.

Overview: In this example, the user will input three purchase orders, with data

234 44 1
17 68 220
4 56 18

After the third purchase order is entered, we will watch as it is inserted into the linked list.

Lines 9-186: Here I have used the list command to list the part of the program on which we will focus.

GetData() gets the data from the user (Lines 14-15), creates a new ListNode ``box,'' points CurrPtr to it (Line 19), and then stores the user's data into that box (Lines 22-24).

Insert() inserts this new box into the linked list. Its main portion is a while loop (Lines 53-74), which is used to traverse the linked list; each iteration of the loop makes us go to the next node in the list (see comment, Line 68).

Line 84: I have set a breakpoint (at Line 22), right after we create the new box and point CurrPtr to it (Line 19). This way I can print out the value of CurrPtr at that time, to know the address of the new box, for reference when we go through Insert() later on. I have asked gdb to skip the first two times we hit this breakpoint (Line 86), since I am only interested in what happens at the third purchase order.

Line 88: OK, we're running now.

Lines 103ff: The user enters the third purchase order (Line 103), and sure enough, gdb does stop at the breakpoint (Line 105). I ask to see what the address of the newly-created box is, and it turns out to be 0x6710 (Lines 107-108).

Now I take advantage of the pause, and set my other breakpoint (Line 109), which is in the function Insert(). This breakpoint (Line 63) is the first non-special-case statement in the while loop mentioned above. We insert this breakpoint so that we can watch closely as the new box is inserted into the list.

Line 111: So, let's get on with it.

Lines 114ff: Now we have hit that breakpoint in Insert(). From this point onward, we will be single-stepping through the code (Lines 129, 133, 143), to get a detailed look. At each line, I want to call Display() (recall this function prints out the whole linked list), but I want to avoid the tedium of repeatedly typing

call Display()

so I make use of gdb's define command (Lines 116-120). From now on, I can indulge my laziness by typing `cld' (Lines 125, 137, 147), which is much shorter to type.

footnote: It is not just laziness. The less I have to bother myself with typing, the better I can concentrate on finding my bug, though again, in this particular case I am not using gdb for debugging purposes.

I am also arranging for the current values of LeadingPtr and TrailingPtr to print out at every step (Lines 121, 123), which I want since they are key variables in this loop. Basically, here is what they are used for (Lines 52-74): Recall that the while loop takes us on a tour of the linked list, one node per loop iteration. During each iteration, LeadingPtr will point to some node in the list, and TrailingPointer to the node right behind that node (Lines 52, 70-71).

Eventually we will reach the insertion point, in between two boxes, which I will call oldbox-left and oldbox-right. Let's call our new box newbox. Right now, oldbox-left is connected forward to oldbox-right. We want to change that so that oldbox-left is connected forward to newbox, and newbox is connected forward to oldbox-right. We can make these connections by keeping in mind the pointers which are pointing to them:

TrailingPtr    oldbox-left     
LeadingPtr     oldbox-right    
CurrPtr        newbox

In Line 64, oldbox-left will be connected forward to newbox, and in Line 65, newbox will be connected forward to oldbox-right.

Lines 125-128: Here we see what the linked list looks like right now (before inserting the new box). As expected, we see that the list consists of two boxes, corresponding to the first two purchase orders. The two boxes are at addresses 66e0 and 66f8.

Note that when we say that the second box is ``connected'' to the first, we do not mean this in any physical sense; we only mean that we have set the Link field of the first box, 66f8, to the address of the second box. This is absolutely meaningless to the hardware--we can put any value into any memory location, including putting the address of one location into another location, and the hardware won't care what we put where--but WE make it meaningful by writing our program to follow this trail. For example, we wrote the Display() function to include a line

TmpPtr = TmpPtr->Link;

(Line 65 of the original struct/pointers handout), which changes TmpPtr from pointing to one box to pointing to the next box in the linked list.

footnote: Kevin Rich made a nice analogy to all of this: Suppose you are playing some kind of game, like a scavenger hunt in a home, in which each clue leads you to the next. Say the clues are in notes inside of envelopes. Your first envelope note might, in addition to giving you your first clue, say, ``Go to the kitchen to get your next clue.'' You go to the kitchen, and find an envelope there. The note in the envelope contains your next clue, and in addition says, ``Your next clue is in the bathroom.'' You then go to the bathroom, where you find another envelope, and so on. The point is that these envelopes can be scattered all over the house, anywhere, not next to each other. And if, say, I want to add a new clue in the garage, that is acquired after the one in the kitchen but before the one in the bathroom, I simply change the note in the kitchen to say, ``Go to the garage,'' and write the note in the garage to say, ``Your next clue is in the bathroom.''

Lines 129ff: Now we are single-stepping through the code which will insert our newest purchase order,

4 56 18

into the list, where the list currently contains the purchase orders

234 44 1
17 68 220

So the new order (which we called newbox above), with time stamp 56, should go between the existing ones with time stamps 44 (oldbox-left) and 68 (oldbox-right).

Line 130: Remember, one of the things we must do is connect the time-44 purchase order (pointed to by TrailingPtr) forward with our new one (pointed to by CurrPtr). That is what this line does.

Line 134: The other thing we must do is connect our new box (pointed to by CurrPtr) forward with our time-68 purchase order (pointed to by LeadingPtr). We do that in this line.

Lines 137ff: OK, let's call Display() to take a look around. There's the time-44 purchase order, still at the head of the list, just as it should be. And there's the time-56 order, the new one, which we now see is successfully connected to the time-44 order (the fifth field on Line 138 is 6710, and the first field in Line 139 is 6710).

But how come the time-68 purchase order has disappeared? Well, it is still there, but it wasn't picked up by our call to Display(), for the following reason: We have written Display(), as mentioned earlier, to step through the linked list, using the Link field in one box to get to the next box. Well, since on Line 133 we executed Line 130, the link that formerly had existed between the time-44 purchase order and the time-68 purchase order (as recently as Lines 126-127) has now been broken, with the time-44 purchase order now connected instead to our new time-56 purchase order.

But to make sure that the time-68 purchase order is still there, I gave the gdb command

p *LeadingPtr

on Line 141. This says, ``print the object pointed to by LeadingPtr.'' That object is the struct which contains our time-68 purchase order, which is now printed out (Line 142).

Line 143: Now we execute Line 134, which will complete the insertion, by literally providing the ``missing link'' that we just discussed above, i.e. we will connect the new box to the time-68 box. To see that it all worked out correctly, we call Display() again (Lines-147-150), and we see that the three purchase orders are indeed connected correctly: The time-44 order has 6710 in its Link field, and the time-56 order is at 6710; the time-56 order has 66f8 in its Link field, and the time-68 order is at 66f8.



Norm Matloff
Wed Nov 8 17:34:51 PST 1995