One useful operation is one that will create a new List_Node object and then return an access value referencing this newly-created object. These new objects will be created in a general storage area and will not automatically disappear just because some subprogram has returned. The process of creating a new object in some storage area is called allocation. In C the allocation operation is called "malloc", while in Pascal and C++ it's called "new". Ada also calls this operation new, and Ada has the same syntax as C++ and Pascal; the keyword "new" is followed by the name of the type to be created. For example, here's how to create a two new objects of type List_Node, setting the value of "Current" to access one and the value of "Root" to the other one:
Current := new List_Node; Root := new List_Node;
At this point, we say that Current accesses a List_Node, and Root accesses a different List_Node.
Once an access value accesses a real object (instead of being null), you can use the ``dot'' operation to refer to the values in the object accessed by the access value. This has the same syntax as using values in a record. For example, since we have created two new nodes, let's set the data for the node "Current" accesses to 1, and the data value for what Root accesses to 2:
Current.Data := 1; Root.Data := 2;
The nice thing about access values is that you can use them to "connect" components together into a more complex structure. You can "connect" components by assigning values to access values. For example, let's connect the nodes we've been using so that the "next" node after what Root accesses is what Current accesses. The way to do that is to change the value of "Root.Next". But what should its new value be? Well, its new value should be same as the value of "Current" so that they access the same node. Here's how to make that happen in Ada:
Root.Next := Current;
If you're not familiar with access values this may cause you some headaches. One way to read that assignment statement is to read it as the following: "change Root.Next so that it now accesses the same thing that Current accesses".
There is a common way to draw such structures. Basically, draw every variable as a box; draw records as boxes subdivided into their record components. For non-access values, just write their value in. For access values, either write "null" (if its value is null) or an arrow from the box to whatever box the value accesses. Whenever you execute a "new" command, draw a new box. An assignment to an access value changes the destination of that arrow; the starting point of the arrow is described on the left hand side of the assignment, and the new destination of the arrow is whatever is referenced by the right hand side of the assignment. The following figure shows what we've done so far:
In some cases you want to work with the "entire" object being accessed instead of just a piece of it. In that case, you use the pseudo-component ".all"; an access value with ".all" after it refers to what it accesses, not the access variable itself. For example, let's say that you have some procedure (My_Procedure) that requires as input a Tree_Node. Declaring such a procedure is easy:
procedure My_Procedure(Input : in Tree_Node);
However, given this particular declaration, you can't just pass in an access value to a Tree_Node, because the types are different (an access value is different than what it accesses). To handle this, simply use the word "all" after the dot operation to refer to the entire object:
Many people get confused about assignment with and without the ".all" phrase. Let's contrast them. Here are two different statements that look similar, but are quite different in meaning:
Root.all := Current.all; -- Statement (1). Root := Current; -- Statement (2).
If you execute statement (1), you will take the entire contents of what Current accesses, and copy those contents to what Root accesses. If you execute statement (2) instead of executing statement (1), you will not affect the underlying node that Root accesses -- you will change the value of Root itself so that Root will now access a different object - the node that Current accesses.
The following figures show how statement (1) and statement (2) differ.
If you're familiar with ANSI C, the following "equivalencies" may help you understand how to use Ada's access types:
Ada Statement or Declaration ANSI C Equivalent -------------------------------- ------------------------------- type Node_Access is access Node; typedef node *node_access; Start : Node_Access; node_access start = 0; Current := new Node; current = malloc(sizeof(node)); Current := Start; current = start; Current.all := Start.all; *current = *start; Current.Data := 5; current->data = 5;
Let's say you execute the following set of statements, where both Current and Root are of type List_Node_Access:
Current := new List_Node; Root := new List_Node; Root.Data := 7; Current.Data := 12; Current := Root; Root.Data := 6;
After executing the statements listed above, what is the value of Current.Data? Hint: This is not an easy question. You may find it easier to answer by using drawings like the ones above.
|Go back to the previous section||Skip to the next section||Go up to lesson 12 outline|
David A. Wheeler (email@example.com)
The master copy of this file is at
The master copy of this file is at "http://www.adahome.com/Tutorials/Lovelace/s12s3.htm".