home *** CD-ROM | disk | FTP | other *** search
/ Chip 1995 March / CHIP3.mdf / programm / prog3 / chap23.txt < prev    next >
Encoding:
Text File  |  1991-07-01  |  20.5 KB  |  469 lines

  1.  
  2.  
  3.  
  4.                                                        Chapter 23
  5.                                                DYNAMIC ALLOCATION
  6.  
  7.  
  8. Although this chapter is titled "Dynamic Allocation", it may be
  9. more proper to title it "Sorting with Linked Lists", since that is
  10. what we will actually do.  This chapter contains example programs
  11. that will illustrate how to generate a linked list with dynamically
  12. allocated entries.  It is meant to illustrate how to put several
  13. programming techniques together in a meaningful manner.  It will
  14. also instruct you in dynamic allocation and deallocation
  15. techniques.
  16.  
  17.  
  18.  
  19. DYNAMIC DEALLOCATION
  20. _________________________________________________________________
  21.  
  22. One of the most important topics covered in this chapter is that
  23. of dynamic deallocation.  After variables are dynamically allocated
  24. and used, they can be deallocated in such a way that the memory
  25. space can be reclaimed for reuse by other variables.  Ada uses two
  26. techniques, one being garbage collection, and the other being
  27. unchecked deallocation.  We will have a good bit to say about both
  28. of these in this chapter.
  29.  
  30.  
  31.  
  32. A SIMPLE LINKED LIST
  33. _________________________________________________________________
  34.  
  35. The program named LINKLIST.ADA contains all of   ================
  36. the logic needed to generate and traverse a        LINKLIST.ADA
  37. linked list.  We will go through it in some      ================
  38. detail to illustrate how to build and use a
  39. linked list.
  40.  
  41. The first thing we need for a linked list is a record type
  42. containing an access variable which accesses itself.  Line 12 is
  43. an incomplete record definition which will allow us to define the
  44. access type in line 14.  After we have the access type defined, we
  45. can complete the record definition in lines 16 through 20 which
  46. includes a reference to the access type.  The record type therefore
  47. contains a reference to itself.  Line 12 is more properly called
  48. a type specification and lines 16 through 20 a type body.  Note
  49. that the incomplete type definition can only be used to declare an
  50. access variable, and for no other purpose.
  51.  
  52. We declare two additional access variables in lines 22 and 23, and
  53. two procedures to be used to generate and traverse the list as we
  54. will see shortly.
  55.  
  56.  
  57.  
  58.                                                         Page 23-1
  59.  
  60.                                   Chapter 23 - Dynamic Allocation
  61.  
  62. The program itself, beginning in line 59, begins with a for loop
  63. covering the range of the string variable named Data_String,
  64. defined earlier, and each character of this string is given in turn
  65. to the procedure Store_Character.  It remains for us to discover
  66. what this procedure does.
  67.  
  68.  
  69.  
  70. THE PROCEDURE Store_Character
  71. _________________________________________________________________
  72.  
  73. We enter this procedure with the single character and wish to store
  74. it away somehow for later use.  We use a local variable, named
  75. Temp, which is an access variable to our record type and use it to
  76. dynamically allocate storage for a variable of the record type
  77. CHAR_REC in line 45, then assign the single character input from
  78. the calling program to its field named One_Letter.  The field of
  79. this record named Next_Rec is an access type variable, and
  80. according to the definition of Ada, the system will set it to null
  81. when it is generated.  Figure 23-1 is a graphical representation
  82. of the record variable, the two access type variables defined in
  83. lines 22 and 23, named Start and Last, and the local access
  84. variable named Temp.  Now we need to insert the new record into our
  85. linked list, but there is a different way to do it depending on
  86. whether it is the first record, or an additional record.
  87.  
  88.  
  89.  
  90. THE FIRST RECORD
  91. _________________________________________________________________
  92.  
  93. If it is the first record, or if this is the first time this
  94. procedure has been called, the value of the access variable Start
  95. will be null, because the system assigned a value of null to it
  96. when it was elaborated.  We can test the value, and if it is null,
  97. we assign the value of the new record's access variable to the
  98. access variable Start and to Last.  We have generated data that
  99. could be graphically depicted by figure 23-2, and we will see
  100. shortly just how this will be used.
  101.  
  102.  
  103.  
  104. AN ADDITIONAL RECORD
  105. _________________________________________________________________
  106.  
  107. If we find that the value of Start is not null, indicating that it
  108. has already been assigned to access another record, we need to add
  109. the new record to the end of the list.  If it is the second time
  110. we have entered this procedure, we have data as pictured in figure
  111. 23-3, which includes the single record entered earlier, and the
  112. new record which is still disassociated with the first but accessed
  113. by the access variable named Temp.  We add the new record to the
  114. end of the list in lines 53 and 54, with the resulting list being
  115. pictured graphically in figure 23-4.  After entering the third
  116.  
  117.                                                         Page 23-2
  118.  
  119.                                   Chapter 23 - Dynamic Allocation
  120.  
  121. element and adding it to the end of the list, we have the data
  122. structure pictured in figure 23- 5.  Further elements will not be
  123. diagrammed for this example, but it would be good for the student
  124. to draw additional diagrams.
  125.  
  126.  
  127.  
  128. TRAVERSING THE LIST
  129. _________________________________________________________________
  130.  
  131. Each time a character is added to the list, the Traverse_List
  132. procedure is called which starts at the input access point, Start
  133. in this program, and lists all characters currently in the list.
  134. It does this through use of its own local access variable named
  135. Temp which is initially assigned the address of the first record
  136. in the list.  It checks first to see if the list is empty, and if
  137. it is not, it enters a loop where it outputs the character in each
  138. record until it finds a record with a value of null in its access
  139. variable field named Next_Rec, which is an access pointer.  The
  140. variable Temp is used to work its way through the list by the
  141. assignment in line 35 where Temp is assigned the access variables
  142. value from the next record each time through the loop.
  143.  
  144. Since the list is traversed each time a character is entered into
  145. the list, the list of characters output will grow by one character
  146. each time a character is added and the updated list is traversed.
  147.  
  148.  
  149.  
  150. MORE ABOUT GARBAGE COLLECTION
  151. _________________________________________________________________
  152.  
  153. We mentioned garbage collection in chapter 13 of this tutorial, but
  154. there is more to be discussed about this subject.  An Ada compiler
  155. may include a garbage collection facility which will search through
  156. the access variables and the dynamic allocation heap occasionally
  157. to see if there are any locations in the heap that are not accessed
  158. by access variables.  If it finds any, it will return those
  159. locations on the heap to the dynamic access pool so they will be
  160. available for allocation again.  In this way, any memory that gets
  161. lost, either through faulty programming, or due to intentionally
  162. clearing an access variable, will be automatically restored and
  163. available for reuse as dynamic variables.  Note however, that
  164. implementation of a garbage collector is optional in an Ada
  165. compiler.  Check your documentation to see if a garbage collector
  166. is actually available with your compiler.
  167.  
  168.  
  169.  
  170. USING THE GARBAGE COLLECTOR
  171. _________________________________________________________________
  172.  
  173. In lines 71 through 76, we execute a loop that will traverse the
  174. linked list, and assign all of the access variables the value of
  175.  
  176.                                                         Page 23-3
  177.  
  178.                                   Chapter 23 - Dynamic Allocation
  179.  
  180. null.  If there is a garbage collector, it will eventually find the
  181. locations on the heap that no longer have an access variable
  182. accessing them and return those locations to the available pool of
  183. usable memory.  We used the word eventually because there is no
  184. predefined time when this will occur, but is at the discretion of
  185. the compiler writer.  More will be said about this topic later in
  186. this chapter.
  187.  
  188. Compile and Execute this program, observe the output, and then
  189. return to additional study if you do not completely understand its
  190. operation.  You will need a good grasp of this program in order to
  191. understand the next program, so when you are ready, we will go on
  192. to a linked list that is a bit more complicated because it can be
  193. used to sort an array of characters alphabetically.
  194.  
  195.  
  196.  
  197. A LINKED LIST THAT SORTS ALPHABETICALLY
  198. _________________________________________________________________
  199.  
  200. The next example program, named SORTLIST.ADA,    ================
  201. uses a different storage technique that results    SORTLIST.ADA
  202. in an alphabetically sorted list.  This program  ================
  203. is identical to the last except for the
  204. Store_Character procedure.
  205.  
  206. In this program, the Store_Character procedure works just like the
  207. last one if it is the first character input as you can see by
  208. inspecting lines 77 through 80.  If it is an additional input
  209. however, we make a call to the embedded procedure named
  210. Locate_And_Store.  This procedure searches through the existing
  211. list looking for the first character in the list that is not less
  212. than the new character alphabetically.  When it is found, the
  213. search is terminated and the new character must be inserted into
  214. the list prior to the located character.  This is done by moving
  215. access variables around rather than by moving actual variables
  216. around.  If the new character must be added to the starting point
  217. of the list, it must be handled in a special way, and if it must
  218. be the last element, it also requires special handling.
  219.  
  220.  
  221.  
  222. ADDING ELEMENTS TO THE LIST
  223. _________________________________________________________________
  224.  
  225. Figure 23-6 illustrates the condition of the data when the fourth
  226. element is to be added to a three element list.  The three element
  227. list is identical to the list described in the last example program
  228. and Temp is accessing the new element to be inserted
  229. alphabetically.  Lines 67 and 68 of the program alter the access
  230. variables to do the insertion, and figure 23-7 illustrates the
  231. result of changing the access variables to achieve the goal.  Note
  232. that the character data used here is not the same as the data used
  233. in the program, but is different for illustrative purposes.  The
  234.  
  235.                                                         Page 23-4
  236.  
  237.                                   Chapter 23 - Dynamic Allocation
  238.  
  239. cases where the new record is added to the beginning, and when it
  240. is added to the end will not be illustrated graphically, but is
  241. left for the student to diagram.
  242.  
  243.  
  244.  
  245. MORE ABOUT Unchecked_Deallocation
  246. _________________________________________________________________
  247.  
  248. In chapter 13, we first mentioned the generic procedure supplied
  249. with your compiler named Unchecked_Deallocation and illustrated how
  250. to use it in example programs there.  Since it can be used with any
  251. dynamically allocated data, it can be used with these programs
  252. also.  In order to use it, you must first mention the name in a
  253. with clause as is done in line 2 of this program to make it
  254. available.  Because it is a generic procedure, it must be
  255. instantiated before use.  Line 27 is the instantiation of the
  256. procedure where it is named Free.  Pascal and C each have a
  257. deallocator available named Free and the name Free has become
  258. nearly standard in Ada because of the other languages.  You would
  259. be encouraged to use the name Free also for ease of communication
  260. with other Ada programmers.  It would be perfectly legal to name
  261. it any other name provided it obeyed the rules of naming an
  262. identifier.
  263.  
  264.  
  265.  
  266. HOW DO YOU USE Unchecked_Deallocation
  267. _________________________________________________________________
  268.  
  269. When you use the Unchecked_Deallocation procedure, you are
  270. essentially taking on the responsibility for managing the heap
  271. yourself, and you would like to let the system know that you will
  272. be responsible for garbage collection, and that it need not concern
  273. itself with it.  You do this by using the pragma named CONTROLLED
  274. as illustrated in line 25.  This tells the system that you will be
  275. responsible for managing all areas of the heap that are dynamically
  276. allocated to any access variable of type CHAR_REC_POINT.
  277.  
  278. You may think that it would be a good idea to let the system
  279. maintain the heap and do the garbage collection automatically but
  280. this can be a real problem, which will be evident after we
  281. understand what garbage collection is and how it is implemented.
  282.  
  283.  
  284.  
  285. HOW IS GARBAGE COLLECTION IMPLEMENTED?
  286. _________________________________________________________________
  287.  
  288. There is no predefined method of how often garbage collection
  289. should be implemented, so it is up to each compiler writer to
  290. define it his own way.  One of the most common methods is to wait
  291. until the heap is used up, then do a search of all access variables
  292. and all heap areas to find all areas that are unreferenced.  Those
  293.  
  294.                                                         Page 23-5
  295.  
  296.                                   Chapter 23 - Dynamic Allocation
  297.  
  298. areas are then returned to the heap and program execution is
  299. resumed.  The biggest problem with this is that it may take as much
  300. as a full second of execution time to accomplish this during which
  301. time the Ada program is essentially stopped.  This is not
  302. acceptable in a real-time system because it could occur at a very
  303. inopportune time, such as during final approach of a 747 into an
  304. international airport.  In that case, you would do well to use the
  305. pragma named CONTROLLED to tell the system to ignore garbage
  306. collection, and manage the heap yourself as we are doing in this
  307. program.
  308.  
  309.  
  310.  
  311. DEALLOCATING THE LIST
  312. _________________________________________________________________
  313.  
  314. The loop in lines 98 through 103 will traverse the list and return
  315. all of the allocated data to the heap where it will be immediately
  316. available for reuse.  The observant student will notice that the
  317. access variable from each record is copied to the variable named
  318. Last prior to deallocating that particular record.
  319.  
  320. Compile and execute this program, so you can see that it really
  321. does sort the input characters alphabetically.  It should be
  322. apparent to you that this very simple case of sorting single
  323. characters is not really too useful in the real world, but sorting
  324. a list of records containing 23 fields, by last name, first name,
  325. zipcode, and place of birth, could be a rather large undertaking,
  326. but could also lead to a very useful program.  Remember that in
  327. this program we are only changing access variables rather than
  328. moving the data around, so the efficiency of this technique when
  329. using it for a large data base should be very good.
  330.  
  331.  
  332.  
  333. A BINARY TREE SORTING PROGRAM
  334. _________________________________________________________________
  335.  
  336. The example file named BTREE.ADA illustrates the  ===============
  337. use of dynamic allocation and recursion to           BTREE.ADA
  338. perform a sorting function.  It uses a binary     ===============
  339. tree method of alphabetization, which is
  340. thoroughly defined in Wirth's book, "Algorithms
  341. + data structures = Programs".  The method will be briefly defined
  342. here.
  343.  
  344. The sorting element is pictured in figure 23-8, where a node is
  345. composed of the stored data within the circle and the two pointers,
  346. each of which point to other nodes or to null values.  Each of
  347. these nodes must have something pointing to it to make the entire
  348. system useful, and we need a few additional pointers to find our
  349. directions through the final overall structure.
  350.  
  351.  
  352.  
  353.                                                         Page 23-6
  354.  
  355.                                   Chapter 23 - Dynamic Allocation
  356.  
  357. The definition of the node is contained in the program in lines 13
  358. through 22, where we define the type of the node with 2 access
  359. variables pointing to itself.  You will see that we have one access
  360. variable named Left and another named Right which correspond to the
  361. two legs out of the bottom of the node in figure 23-8.  The node
  362. itself contains the data which could be any number of different
  363. variables including arrays and records, but for our purposes of
  364. illustration, will contain only a single character.
  365.  
  366.  
  367.  
  368. BUILDING THE TREE
  369. _________________________________________________________________
  370.  
  371. The main program begins in line 77 with a loop to call the
  372. procedure Store_Character once with each character in the input
  373. string named Data_String.  The following description of operation
  374. will use Test_String as the string example.  The first time we call
  375. Store_Character, with the character "D", we allocate a new record,
  376. store the "D" in it, and since Root is equal to null, we execute
  377. line 68 to assign the access variable named Root to point to the
  378. new record, resulting in the state found in figure 23-9.
  379.  
  380. The next time we call Store_Character, this time with the character
  381. "B", we allocate a new record, store the "B" in it, and since Root
  382. is no longer equal to null, we call the procedure Locate_And_Store
  383. from line 70, telling it to start at Root.  The procedure
  384. Locate_And_Store is recursive, calling itself successively until
  385. it successfully stores the character.  The first time it is called,
  386. the input character is less than the stored character at the input
  387. node, which is "D", so the left branch is chosen, and the statement
  388. in lines 48 through 52 is executed.  In this particular case, the
  389. left branch is null, so it is assigned the address of the input
  390. record resulting in the state found in figure 23- 10.  The tree is
  391. beginning to take shape.
  392.  
  393.  
  394. THE THIRD CHARACTER
  395. _________________________________________________________________
  396.  
  397. The next character is sent to Store_Character, this time a "C",
  398. resulting in another call to Locate_And_Store.  On this pass
  399. through the logic, because the input character is less than the
  400. character at the root node, we select the left branch and call
  401. Locate_And_Store recursively from line 51.  On this recursive call,
  402. we tell it to work with the node stored at the left branch of the
  403. present node.  In the next procedure call, we find that "C" is not
  404. less than the "B" stored there and we find that the right branch
  405. of this node is null, so we can store the new node there.  Our tree
  406. now looks like that given in figure 23-11.
  407.  
  408. Continuing through the remaining characters of our input stream,
  409. we finally have the structure as pictured in figure 23-12 with all
  410. 6 characters stored in it.
  411.  
  412.                                                         Page 23-7
  413.  
  414.                                   Chapter 23 - Dynamic Allocation
  415.  
  416.  
  417. TRAVERSING THE B-TREE
  418. _________________________________________________________________
  419.  
  420. Traversing the tree is essentially the same as building it, except
  421. that we do not need to test for equality of the input data, only
  422. test each node's Left and Right access values.  If the access
  423. values are not equal to null, there is a subtree where the access
  424. variable is pointing, and we recurse to that subtree and check each
  425. of its subtrees.  With a bit of study, you should be able to trace
  426. your way through the tree to see that we actually do alphabetize
  427. the input characters by the way we built the tree, then traverse
  428. it for output.
  429.  
  430.  
  431.  
  432. DEALLOCATING THE TREE
  433. _________________________________________________________________
  434.  
  435. Once again we use Unchecked_Deallocation and the pragma CONTROLLED
  436. to explicitly deallocate the tree.  We do this by traversing the
  437. tree in a manner similar to when we printed the characters out.
  438. One important thing to keep in mind however is that you must free
  439. a node only after you have checked both subtrees, because once you
  440. free a node, its subtrees are no longer available.
  441.  
  442.  
  443.  
  444. PROGRAMMING EXERCISES
  445. _________________________________________________________________
  446.  
  447. 1.   Use Unchecked_Deallocation to deallocate the list in the
  448.      example program LINKLIST.ADA.
  449.  
  450. 2.   Add a variable of type INTEGER named Character_Count to the
  451.      record named B_TREE_NODE in BTREE.ADA.  Store the current
  452.      character count in this variable as the tree is generated.
  453.      When the string is completed, output the sorted character list
  454.      along with the position in the string it occupies.
  455.  
  456.         B is character 2 in the string.
  457.         C is character 3 in the string.
  458.         D is character 1 in the string.
  459.         F is character 6 in the string.
  460.         G is character 4 in the string.
  461.         I is character 5 in the string.
  462.  
  463.  
  464.  
  465.  
  466.  
  467.  
  468.  
  469.                                                         Page 23-8