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.
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.
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.