home *** CD-ROM | disk | FTP | other *** search
-
-
-
- Chapter 23
- DYNAMIC ALLOCATION
-
-
- Although this chapter is titled "Dynamic Allocation", it may be
- more proper to title it "Sorting with Linked Lists", since that is
- what we will actually do. This chapter contains example programs
- that will illustrate how to generate a linked list with dynamically
- allocated entries. It is meant to illustrate how to put several
- programming techniques together in a meaningful manner. It will
- also instruct you in dynamic allocation and deallocation
- techniques.
-
-
-
- DYNAMIC DEALLOCATION
- _________________________________________________________________
-
- One of the most important topics covered in this chapter is that
- of dynamic deallocation. After variables are dynamically allocated
- and used, they can be deallocated in such a way that the memory
- space can be reclaimed for reuse by other variables. Ada uses two
- techniques, one being garbage collection, and the other being
- unchecked deallocation. We will have a good bit to say about both
- of these in this chapter.
-
-
-
- A SIMPLE LINKED LIST
- _________________________________________________________________
-
- The program named LINKLIST.ADA contains all of ================
- the logic needed to generate and traverse a LINKLIST.ADA
- linked list. We will go through it in some ================
- detail to illustrate how to build and use a
- linked list.
-
- The first thing we need for a linked list is a record type
- containing an access variable which accesses itself. Line 12 is
- an incomplete record definition which will allow us to define the
- access type in line 14. After we have the access type defined, we
- can complete the record definition in lines 16 through 20 which
- includes a reference to the access type. The record type therefore
- contains a reference to itself. Line 12 is more properly called
- a type specification and lines 16 through 20 a type body. Note
- that the incomplete type definition can only be used to declare an
- access variable, and for no other purpose.
-
- We declare two additional access variables in lines 22 and 23, and
- two procedures to be used to generate and traverse the list as we
- will see shortly.
-
-
-
- Page 23-1
-
- Chapter 23 - Dynamic Allocation
-
- The program itself, beginning in line 59, begins with a for loop
- covering the range of the string variable named Data_String,
- defined earlier, and each character of this string is given in turn
- to the procedure Store_Character. It remains for us to discover
- what this procedure does.
-
-
-
- THE PROCEDURE Store_Character
- _________________________________________________________________
-
- We enter this procedure with the single character and wish to store
- it away somehow for later use. We use a local variable, named
- Temp, which is an access variable to our record type and use it to
- dynamically allocate storage for a variable of the record type
- CHAR_REC in line 45, then assign the single character input from
- the calling program to its field named One_Letter. The field of
- this record named Next_Rec is an access type variable, and
- according to the definition of Ada, the system will set it to null
- when it is generated. Figure 23-1 is a graphical representation
- of the record variable, the two access type variables defined in
- lines 22 and 23, named Start and Last, and the local access
- variable named Temp. Now we need to insert the new record into our
- linked list, but there is a different way to do it depending on
- whether it is the first record, or an additional record.
-
-
-
- THE FIRST RECORD
- _________________________________________________________________
-
- If it is the first record, or if this is the first time this
- procedure has been called, the value of the access variable Start
- will be null, because the system assigned a value of null to it
- when it was elaborated. We can test the value, and if it is null,
- we assign the value of the new record's access variable to the
- access variable Start and to Last. We have generated data that
- could be graphically depicted by figure 23-2, and we will see
- shortly just how this will be used.
-
-
-
- AN ADDITIONAL RECORD
- _________________________________________________________________
-
- If we find that the value of Start is not null, indicating that it
- has already been assigned to access another record, we need to add
- the new record to the end of the list. If it is the second time
- we have entered this procedure, we have data as pictured in figure
- 23-3, which includes the single record entered earlier, and the
- new record which is still disassociated with the first but accessed
- by the access variable named Temp. We add the new record to the
- end of the list in lines 53 and 54, with the resulting list being
- pictured graphically in figure 23-4. After entering the third
-
- Page 23-2
-
- Chapter 23 - Dynamic Allocation
-
- element and adding it to the end of the list, we have the data
- structure pictured in figure 23- 5. Further elements will not be
- diagrammed for this example, but it would be good for the student
- to draw additional diagrams.
-
-
-
- TRAVERSING THE LIST
- _________________________________________________________________
-
- Each time a character is added to the list, the Traverse_List
- procedure is called which starts at the input access point, Start
- in this program, and lists all characters currently in the list.
- It does this through use of its own local access variable named
- Temp which is initially assigned the address of the first record
- in the list. It checks first to see if the list is empty, and if
- it is not, it enters a loop where it outputs the character in each
- record until it finds a record with a value of null in its access
- variable field named Next_Rec, which is an access pointer. The
- variable Temp is used to work its way through the list by the
- assignment in line 35 where Temp is assigned the access variables
- value from the next record each time through the loop.
-
- Since the list is traversed each time a character is entered into
- the list, the list of characters output will grow by one character
- each time a character is added and the updated list is traversed.
-
-
-
- MORE ABOUT GARBAGE COLLECTION
- _________________________________________________________________
-
- We mentioned garbage collection in chapter 13 of this tutorial, but
- there is more to be discussed about this subject. An Ada compiler
- may include a garbage collection facility which will search through
- the access variables and the dynamic allocation heap occasionally
- to see if there are any locations in the heap that are not accessed
- by access variables. If it finds any, it will return those
- locations on the heap to the dynamic access pool so they will be
- available for allocation again. In this way, any memory that gets
- lost, either through faulty programming, or due to intentionally
- clearing an access variable, will be automatically restored and
- available for reuse as dynamic variables. Note however, that
- implementation of a garbage collector is optional in an Ada
- compiler. Check your documentation to see if a garbage collector
- is actually available with your compiler.
-
-
-
- USING THE GARBAGE COLLECTOR
- _________________________________________________________________
-
- In lines 71 through 76, we execute a loop that will traverse the
- linked list, and assign all of the access variables the value of
-
- Page 23-3
-
- Chapter 23 - Dynamic Allocation
-
- null. If there is a garbage collector, it will eventually find the
- locations on the heap that no longer have an access variable
- accessing them and return those locations to the available pool of
- usable memory. We used the word eventually because there is no
- predefined time when this will occur, but is at the discretion of
- the compiler writer. More will be said about this topic later in
- this chapter.
-
- Compile and Execute this program, observe the output, and then
- return to additional study if you do not completely understand its
- operation. You will need a good grasp of this program in order to
- understand the next program, so when you are ready, we will go on
- to a linked list that is a bit more complicated because it can be
- used to sort an array of characters alphabetically.
-
-
-
- A LINKED LIST THAT SORTS ALPHABETICALLY
- _________________________________________________________________
-
- The next example program, named SORTLIST.ADA, ================
- uses a different storage technique that results SORTLIST.ADA
- in an alphabetically sorted list. This program ================
- is identical to the last except for the
- Store_Character procedure.
-
- In this program, the Store_Character procedure works just like the
- last one if it is the first character input as you can see by
- inspecting lines 77 through 80. If it is an additional input
- however, we make a call to the embedded procedure named
- Locate_And_Store. This procedure searches through the existing
- list looking for the first character in the list that is not less
- than the new character alphabetically. When it is found, the
- search is terminated and the new character must be inserted into
- the list prior to the located character. This is done by moving
- access variables around rather than by moving actual variables
- around. If the new character must be added to the starting point
- of the list, it must be handled in a special way, and if it must
- be the last element, it also requires special handling.
-
-
-
- ADDING ELEMENTS TO THE LIST
- _________________________________________________________________
-
- Figure 23-6 illustrates the condition of the data when the fourth
- element is to be added to a three element list. The three element
- list is identical to the list described in the last example program
- and Temp is accessing the new element to be inserted
- alphabetically. Lines 67 and 68 of the program alter the access
- variables to do the insertion, and figure 23-7 illustrates the
- result of changing the access variables to achieve the goal. Note
- that the character data used here is not the same as the data used
- in the program, but is different for illustrative purposes. The
-
- Page 23-4
-
- Chapter 23 - Dynamic Allocation
-
- cases where the new record is added to the beginning, and when it
- is added to the end will not be illustrated graphically, but is
- left for the student to diagram.
-
-
-
- MORE ABOUT Unchecked_Deallocation
- _________________________________________________________________
-
- In chapter 13, we first mentioned the generic procedure supplied
- with your compiler named Unchecked_Deallocation and illustrated how
- to use it in example programs there. Since it can be used with any
- dynamically allocated data, it can be used with these programs
- also. In order to use it, you must first mention the name in a
- with clause as is done in line 2 of this program to make it
- available. Because it is a generic procedure, it must be
- instantiated before use. Line 27 is the instantiation of the
- procedure where it is named Free. Pascal and C each have a
- deallocator available named Free and the name Free has become
- nearly standard in Ada because of the other languages. You would
- be encouraged to use the name Free also for ease of communication
- with other Ada programmers. It would be perfectly legal to name
- it any other name provided it obeyed the rules of naming an
- identifier.
-
-
-
- HOW DO YOU USE Unchecked_Deallocation
- _________________________________________________________________
-
- When you use the Unchecked_Deallocation procedure, you are
- essentially taking on the responsibility for managing the heap
- yourself, and you would like to let the system know that you will
- be responsible for garbage collection, and that it need not concern
- itself with it. You do this by using the pragma named CONTROLLED
- as illustrated in line 25. This tells the system that you will be
- responsible for managing all areas of the heap that are dynamically
- allocated to any access variable of type CHAR_REC_POINT.
-
- You may think that it would be a good idea to let the system
- maintain the heap and do the garbage collection automatically but
- this can be a real problem, which will be evident after we
- understand what garbage collection is and how it is implemented.
-
-
-
- HOW IS GARBAGE COLLECTION IMPLEMENTED?
- _________________________________________________________________
-
- There is no predefined method of how often garbage collection
- should be implemented, so it is up to each compiler writer to
- define it his own way. One of the most common methods is to wait
- until the heap is used up, then do a search of all access variables
- and all heap areas to find all areas that are unreferenced. Those
-
- Page 23-5
-
- Chapter 23 - Dynamic Allocation
-
- areas are then returned to the heap and program execution is
- resumed. The biggest problem with this is that it may take as much
- as a full second of execution time to accomplish this during which
- time the Ada program is essentially stopped. This is not
- acceptable in a real-time system because it could occur at a very
- inopportune time, such as during final approach of a 747 into an
- international airport. In that case, you would do well to use the
- pragma named CONTROLLED to tell the system to ignore garbage
- collection, and manage the heap yourself as we are doing in this
- program.
-
-
-
- DEALLOCATING THE LIST
- _________________________________________________________________
-
- The loop in lines 98 through 103 will traverse the list and return
- all of the allocated data to the heap where it will be immediately
- available for reuse. The observant student will notice that the
- access variable from each record is copied to the variable named
- Last prior to deallocating that particular record.
-
- Compile and execute this program, so you can see that it really
- does sort the input characters alphabetically. It should be
- apparent to you that this very simple case of sorting single
- characters is not really too useful in the real world, but sorting
- a list of records containing 23 fields, by last name, first name,
- zipcode, and place of birth, could be a rather large undertaking,
- but could also lead to a very useful program. Remember that in
- this program we are only changing access variables rather than
- moving the data around, so the efficiency of this technique when
- using it for a large data base should be very good.
-
-
-
- A BINARY TREE SORTING PROGRAM
- _________________________________________________________________
-
- The example file named BTREE.ADA illustrates the ===============
- use of dynamic allocation and recursion to BTREE.ADA
- perform a sorting function. It uses a binary ===============
- tree method of alphabetization, which is
- thoroughly defined in Wirth's book, "Algorithms
- + data structures = Programs". The method will be briefly defined
- here.
-
- The sorting element is pictured in figure 23-8, where a node is
- composed of the stored data within the circle and the two pointers,
- each of which point to other nodes or to null values. Each of
- these nodes must have something pointing to it to make the entire
- system useful, and we need a few additional pointers to find our
- directions through the final overall structure.
-
-
-
- Page 23-6
-
- Chapter 23 - Dynamic Allocation
-
- The definition of the node is contained in the program in lines 13
- through 22, where we define the type of the node with 2 access
- variables pointing to itself. You will see that we have one access
- variable named Left and another named Right which correspond to the
- two legs out of the bottom of the node in figure 23-8. The node
- itself contains the data which could be any number of different
- variables including arrays and records, but for our purposes of
- illustration, will contain only a single character.
-
-
-
- BUILDING THE TREE
- _________________________________________________________________
-
- The main program begins in line 77 with a loop to call the
- procedure Store_Character once with each character in the input
- string named Data_String. The following description of operation
- will use Test_String as the string example. The first time we call
- Store_Character, with the character "D", we allocate a new record,
- store the "D" in it, and since Root is equal to null, we execute
- line 68 to assign the access variable named Root to point to the
- new record, resulting in the state found in figure 23-9.
-
- The next time we call Store_Character, this time with the character
- "B", we allocate a new record, store the "B" in it, and since Root
- is no longer equal to null, we call the procedure Locate_And_Store
- from line 70, telling it to start at Root. The procedure
- Locate_And_Store is recursive, calling itself successively until
- it successfully stores the character. The first time it is called,
- the input character is less than the stored character at the input
- node, which is "D", so the left branch is chosen, and the statement
- in lines 48 through 52 is executed. In this particular case, the
- left branch is null, so it is assigned the address of the input
- record resulting in the state found in figure 23- 10. The tree is
- beginning to take shape.
-
-
- THE THIRD CHARACTER
- _________________________________________________________________
-
- The next character is sent to Store_Character, this time a "C",
- resulting in another call to Locate_And_Store. On this pass
- through the logic, because the input character is less than the
- character at the root node, we select the left branch and call
- Locate_And_Store recursively from line 51. On this recursive call,
- we tell it to work with the node stored at the left branch of the
- present node. In the next procedure call, we find that "C" is not
- less than the "B" stored there and we find that the right branch
- of this node is null, so we can store the new node there. Our tree
- now looks like that given in figure 23-11.
-
- Continuing through the remaining characters of our input stream,
- we finally have the structure as pictured in figure 23-12 with all
- 6 characters stored in it.
-
- Page 23-7
-
- Chapter 23 - Dynamic Allocation
-
-
- TRAVERSING THE B-TREE
- _________________________________________________________________
-
- Traversing the tree is essentially the same as building it, except
- that we do not need to test for equality of the input data, only
- test each node's Left and Right access values. If the access
- values are not equal to null, there is a subtree where the access
- variable is pointing, and we recurse to that subtree and check each
- of its subtrees. With a bit of study, you should be able to trace
- your way through the tree to see that we actually do alphabetize
- the input characters by the way we built the tree, then traverse
- it for output.
-
-
-
- DEALLOCATING THE TREE
- _________________________________________________________________
-
- Once again we use Unchecked_Deallocation and the pragma CONTROLLED
- to explicitly deallocate the tree. We do this by traversing the
- tree in a manner similar to when we printed the characters out.
- One important thing to keep in mind however is that you must free
- a node only after you have checked both subtrees, because once you
- free a node, its subtrees are no longer available.
-
-
-
- PROGRAMMING EXERCISES
- _________________________________________________________________
-
- 1. Use Unchecked_Deallocation to deallocate the list in the
- example program LINKLIST.ADA.
-
- 2. Add a variable of type INTEGER named Character_Count to the
- record named B_TREE_NODE in BTREE.ADA. Store the current
- character count in this variable as the tree is generated.
- When the string is completed, output the sorted character list
- along with the position in the string it occupies.
-
- B is character 2 in the string.
- C is character 3 in the string.
- D is character 1 in the string.
- F is character 6 in the string.
- G is character 4 in the string.
- I is character 5 in the string.
-
-
-
-
-
-
-
- Page 23-8