THE ACCESS TYPE VARIABLE
THE ACCESS TYPE IS DIFFERENT
Example program ------> e_c13_p1.ada
The access type variable is different from every other type we have encountered because it is not actually a variable which can store a piece of data, but contains the address of another piece of data which can be manipulated. As always the best teacher is an example, so examine the file named e_c13_p1.ada for an example program with a few access type variables in it.
DECLARING AN ACCESS TYPE VARIABLE
In line 7 we declare a new type, an access type. As with all types, the reserved word type is given, followed by the type name, then the reserved words is and the type definition. The type definition begins with the reserved word access, which denotes an access type variable, then by the type which we wish to access. The type which we wish to access can be any type which has been declared prior to this point in the program, either a predeclared type or a type we have declared. Because there are no predeclared access types available in Ada, we must declare all access types we wish to use.
The new type is used to declare three access variables in line 8. No actual variables are declared, only access to three places in memory which actually do not exist yet. The access type variables do not store an integer value, as might be expected, but instead store the address of an integer value located somewhere within the address space of the computer memory. Note that the three access variables are automatically initialized to null when they are declared, as are all access variables in Ada.
Figure 13-1 illustrates graphically the condition of the system at this point. A box with a dot in the center depicts an access variable and an empty box will be used to depict a scalar variable.
WE NEED SOME DATA TO POINT AT
As we begin the executable part of the program, we have no data to access, so we create some data storage in line 11 using the new reserved word. This tells the system to go somewhere and create a variable of type INTEGER, with no name, and cause the access variable named Index to point at this new variable. The effective address of the new variable is assigned to the access variable Index, but the new variable still has no assigned value.
Line 12 tells the system to assign the value of 13 to the new variable by using a very strange looking method of doing so. For the first two example programs in this chapter, we will simply say that the value of all of the variable is set to the indicated value, namely 13. The end result is that the access variable named Index is pointing someplace in memory which has no name, but contains the value of 13. Figure 13-2 illustrates our current situation.
Lines 13 through 15 indicate that it is possible to display the value of this variable using the same method we have used in all earlier lessons of this tutorial. If you remember to add the .all to the access variable, you will be referring to the data stored at the location which it accesses.
Line 17 is used to create another variable of type INTEGER somewhere in memory, with Arrow pointing to it, but which contains no value as yet. The next line says to take the value that is stored at the location to which Index points, add 16 to it, and store the result, which should be 29, in the location to which Arrow points. Arrow actually "accesses" the variable, but it is a bit more descriptive to use the word points, especially if you are a Pascal or C programmer.
THE THIRD ACCESS VARIABLE
We have not yet used the access variable named There, so we instruct the system, in line 19, to cause it to point to the same piece of data which Arrow is currently accessing. By failing to add the all to the two access variables, we are assigning the access address to There rather than the value which Arrow accesses. If only one of them had the all appended in line 19, there would be a type clash resulting in a compiler error message. All three access variables are accessing some data somewhere, so we can use their .all notation to display all three values. See figure 13-3 for a graphic representation of the current data space.
Note that there are actually only two variables, because two of the access variables are pointing at the same piece of data. This is illustrated when one of the variables is changed in line 26, and when the data is printed, it is evident that two of the values are different.
Be sure to compile and run this program. When you think you understand it, see if you can modify it such that all three access variables point to the same piece of data.
ACCESSING INTEGER AND FLOAT TYPE VARIABLES
Example program ------> e_c13_p2.ada
Examine the file named e_c13_p2.ada for some additional examples of access type variables. We begin by declaring two access variables which access INTEGER type variables and three access variables that access FLOAT type variables. It should be pointed out, and it probably comes as no surprise to you, that it is illegal to attempt to access a variable with the wrong type of access variable. Explicit type conversion is possible concerning the data types, but not the access types.
Line 14 introduces a new construct, that of initializing a variable when it is created. Using a form similar to qualification, an INTEGER type variable is created somewhere in memory, initialized to 173, and the access variable named Index is assigned its address so that it points to it, or accesses it. After executing line 15, the data space is as shown in figure 13-4.
Be sure to note the difference between the expressions in lines 21 and 22. In line 21, the value stored at the place where Index points, is stored at the place where Arrow points. However, in line 22, the access variable Index is caused to point to the same location where the access variable Arrow points.
A FLOAT TYPE ACCESS VARIABLE
Line 24 illustrates creation of a FLOAT type variable initialized with the value of Pi. Since the access variable names are used in lines 25 and 26 without the all appended, all three FLOAT type access variables are assigned to access the same variable, and some results are displayed. Figure 13-5 illustrates the condition of the system at this point.
The single FLOAT type variable is doubled in line 33, and it is displayed again three different ways. Be sure to compile and execute this program.
ACCESSING A RECORD VARIABLE
Example program ------> e_c13_p3.ada
Examine the example program named e_c13_p3.ada for some additional uses for access variables. This program begins by defining a record type, then an access type which can be used to access data of this record type. Ignore the procedure Free in line 16 for a short time. In line 19, we declare a variable named Myself which is an access variable that accesses a variable of the type MY_RECORD. Since the record does not exist yet, the access variable is actually pointing nowhere. According to the Ada definition, the created access variable will be initialized to the value null, which means it points nowhere. This value can be tested for some value as we shall see later. All access variables used in an Ada program, regardless of how they are declared, will be initially assigned the value of null. This is true, unless they are specifically initialized to some value as shown in line 20.
Line 20 is very interesting because we declare an access variable named Friend, and initialize the access variable by creating a new record somewhere in memory, then initialize the record itself to the values given in the positional aggregate. Finally, the access variable Friend is caused to point to the newly created record. It is permissible to create a new record, but omit the initialization, supplying the initial values in the executable part of the program as we are doing with the variable named Myself. We finally declare a BOOLEAN type variable for later use. Figure 13-6 illustrates our current data space.
USING THE RECORD ACCESS VARIABLE
In line 26, we create a new variable of type MY_RECORD, which is composed of three separate fields. The three fields are assigned in much the same manner that they were assigned in the chapter where we studied records, so this should pose no problem for you. In line 32, we create another new record somewhere and initialize it to the values given, and cause the variable named Friend to point to it, or access it. See figure 13-7.
NOW WE HAVE SOME LOST VARIABLES
Consider that the access variable Friend already had some data that it was pointing at, and we told the system to cause it to point at this newly created record. The record it was formerly pointing at is now somewhere in memory, but has nothing pointing at it, so it is in effect lost. We cannot store anything in it, nor can we read out the data that is stored in it. Of even more consequence, we cannot free up those memory locations for further use, so the space is totally lost to our program. Of course, the operating system will take care of cleaning up all of the lost variables when our program terminates, so the data is not lost forever. It is up to us to see that we do not lose memory space through clumsy programming because Ada cannot check to see that we have reassigned an access variable.
The next interesting thing is illustrated in line 40 where all of the fields of the record which Myself accesses are assigned to all of the fields of the record which Friend accesses. Now it makes sense why the designers of Ada chose to refer to the data which is accessed by the .all notation, it refers to all of the data that the access variable points to. It should be pointed out that as you gain experience with Ada you will find that nearly all access type variables are used to access records, and few, if any, will access scalar variables.
BOOLEAN OPERATIONS WITH ACCESS VARIABLES
Records accessed by access variables can be compared for equality or inequality, just like regular records, and they are equal only if all fields in one record are equal to the corresponding fields in the other record. Line 42 will therefore result in TRUE, because the records are identical, due to the assignment in line 40. Access variables can also be compared to each other, and result in TRUE only if they are both pointing to the same object. In line 43, the result is FALSE because they are pointing to different records, even though the records they point to happen to be equal to each other.
WHAT IS UNCHECKED DEALLOCATION?
The procedure Unchecked_Deallocation is a required part of Ada, so your compiler writer has supplied you with this procedure as a part of the library. Any dynamically allocated data can be freed up for reuse by the system through use of this procedure as illustrated in this program. You must first instantiate a copy of the generic procedure as illustrated in line 16, and name it any available identifier you choose. You must supply two types as parameters, the first being the object type you wish to deallocate, and the second being the access type. The name Free is generally used because that name is used for the equivalent procedure in Pascal and in C.
To actually deallocate some storage, you use the name of the access variable which is accessing the storage to be released as the only parameter of the procedure named Free as illustrated in lines 46 and 47. This storage is then available for reuse by some other part of the program.
There is a lot more to be said about deallocation of storage and the way it is accomplished, but the details will be left until chapter 23, after you gain more experience with Ada. Until then, with your limited knowledge of Ada, you will probably not be writing programs in which you will need this information. Be sure to compile and execute this program.
GARBAGE COLLECTION
Another way to deallocate the data accessed by the access variables, is to assign the value of null to the access variables. This will cause the dynamically allocated variables to have no access variables accessing them, and they are then unusable, or garbage. An Ada implementation may implement a garbage collector to search for un-accessed data and reclaim the storage for further use. Implementation of a garbage collector is optional according to the ARM. Much more will be said about deallocation and garbage collection in chapter 25.
ACCESSING AN ARRAY OF DATA
Example program ------> e_c13_p4.ada
The example program named e_c13_p4.ada gives an example of using an access variable to access an array. The only thing that could be considered new here is the assignment in line 20 where the value of the array List_Of_Stuff, is assigned to the variable which is accessed by the access variable There. Note that 6 INTEGER type values are actually assigned in this one statement.
Note that Unchecked_Deallocation is illustrated here also as an example. The program should be simple for you to follow, and after you understand it, compile and execute it.
AN ARRAY OF ACCESS VARIABLES
Example program ------> e_c13_p5.ada
Examine the program e_c13_p5.ada for an example including an array of access variables which is declared in line 17. The variable named Class is composed of a total of ten access type variables, any of which can be used to point to a variable of type MY_RECORD. The loop in lines 26 through 29 is used to first create a record variable, then assign values to the fields of the created record variable, for each of the ten access variables. Since each record is composed of three subfields, a total of 30 separate variables are created and assigned values in this loop. A few of the variables are reassigned values in lines 31 through 37 for illustrative purposes, and the entire array named Class is displayed on the monitor.
Note that the array declared in line 17 is an anonymous type array.
Compile and run this program, and be sure you understand the resulting printout. The access variable will be very important when we study some of the advanced programming techniques later in this tutorial.
ACCESS TO STATIC OBJECTS
Example program ------> e_c13_p6.ada
Ada 95 has the ability to access a local or global variable with an access variable. This was not permitted with Ada 83. In order to do so, it is necessary to identify the access variable as one with the ability to access a local or global variable by using the keyword all as is done in lines 13 through 15 of the example program. In addition, the variables to be accessed must be identified as accessible with the new keyword aliased as illustrated in lines 18, 21, and 24 of this program. This is a practice that could be easily abused, so the designers of Ada force you to identify the access variable and the variable to be accessed prior to actually doing the operation. This forces you to think carefully about what you are doing. The use and abuse of pointers is the weakest spot in some other languages and lead to very difficult to find errors.
You will note that the Ada compiler will complain, via an error, if any of the ingredients is missing while using this technique. Lines 30 through 32 are included in this program to illustrate the various rules of usage.
There is one other rule that must be followed and is checked by the compiler anytime you use an access variable to refer to a local or global variable. The object referred to must exist for as long or longer than the access variable itself, or the compiler will issue an error message. This is to prevent the error of using an access variable when the object it refers to is no longer in existence.
ACCESS TO SUBPROGRAMS
Example program ------> e_c13_p7.ada
The example program named e_c13_p7.ada is an example of using a single access variable to call three different functions. There are three functions defined in lines 7 through 20, and each have the same number of parameters, the same type of parameters and the same return type. With all of this parallelism, they are candidates for use with an access variable. In line 22 we define a type which happens to have the same signature as the three functions listed above it, and finally we declare an access variable of the new type named Multiply. In the executable part of the program we call the three functions with the access variable in lines 31, 36, and 41. The same call results in calls to different physical functions since it is pointing to a different function each time we call it. Lines 29, 34, and 39 are where we actually assign the address to the access variable.
This same technique can be used with procedure calls as well, provided that the signature for each of the entities is identical. In the case of the procedure, all of the parameters must have the same mode. They are by definition, all of the in mode in a function.
There is one other rule that must be followed and is checked by the compiler anytime you use an access variable to refer to a subprogram. The object referred to must exist for as long or longer than the access variable itself, or the compiler will issue an error message. This is to prevent the use of an access variable when the object it refers to is no longer in existence, an obvious error. In those cases when you think it is all right to override this rule, Unchecked_Access is available for use and documented in the ARM.
The use of anything that is unchecked is highly discouraged.
PROGRAMMING EXERCISES
Return to the Table of Contents