BLOCKS AND SCOPE OF VARIABLES
LARGE PROJECT DECOMPOSITION
Since Ada is a highly structured language, it has the means to divide a large project into many smaller projects through use of procedures and functions. Because procedures and functions can be nested within other procedures and functions, we have the problem of visibility and scope of types, variables, constants, and subprograms.
WHAT IS THE SCOPE OF A VARIABLE?
Example program ------> e_c09_p1.ada
Examine the program named e_c09_p1.ada for several examples of variables with different scopes. You should spend a few minutes familiarizing yourself with the structure of the program which contains the main program, or procedure, and four procedures embedded within it. We will begin with the variable named Count declared in line 4, and state that it has a scope which extends from the semicolon at the end of its declaration to the end of the entire program in line 32. Its scope extends to the end of the program because it is declared in the declaration part of the main program. It is commonly referred to as a global variable.
WHERE IS A VARIABLE VISIBLE?
The variable named Count, declared in line 4, is visible anyplace in the range of its scope, except for one small area of the program. Since another variable with the same name is defined in line 10, the first one is effectively hidden from view within the range of the newer, local variable. Note that it is the local variable that takes precedence and hides the global variable, rather than the other way. The variable named Count from line 4, is not visible from the end of line 10 through the end of line 13. It should be clear that the scope of the local variable extends to the end of the executable portion of the subprogram in which it is declared.
In like manner, the variable named Index, defined in line 7, has a scope that extends from the end of line 7 to the end of its procedure which ends in line 23. The variable named Index is visible throughout its range, because there are no other variables of the same name in a lower level subprogram. The variable named Data is visible throughout its range which extends from the end of line 16 through the end of line 19.
THAT WAS ACTUALLY A LIE
The global variable Count is not visible from lines 10 through 13, but there is a way to use it in spite of its hidden nature. This will be the topic of the next example program, but you should compile and run the present program to see that it really will compile as given. There is no output, so execution will be uninteresting.
USING THE DOT NOTATION
Example program ------> e_c09_p2.ada
Examine the program named e_c09_p2.ada for some examples of making an invisible variable visible. The careful observer will notice that this is the structure of the last program with additional variables declared, and some added assignment statements.
We will consider three variables of the same name, Count, and see that we can use all three variables in a single statement if we so desire. Assume we are at line 12 in the program where we wish to use the local variable named Count, the one that was declared in line 10. By the definition of Ada, the innermost variable will take precedence and by simply using the name Count, we are using the desired one. If however, we would like to use the one declared in line 4, we can do so by using the "dot" notation illustrated in line 13. We are giving the compiler a complete map on where to find the variable. The dot notation can be read as follows, "Go to the outer level, Scope2, dot, and the variable named Count." Line 13 is therefore referring to the variable declared in line 4. Using the notation Scope2.Level1.Count would refer to the variable declared in line 7.
Additional examples of the use of dot notation to use otherwise invisible variables are given in lines 21 through 30. This is also called the expanded name of the variable or the expanded naming convention.
RENAMING A VARIABLE
In order to reduce the number of keystrokes used and to improve the clarity of some programs, Ada provides a renaming capability. Line 18 illustrates this by renaming the triple component combination to the much simpler name, Outer_Index. Anyplace in the procedure where it is permissible to use the longer name, it is also legal to use the new shorter name, because they are simply synonyms for the same actual variable. This is a construct that could easily be abused in a program and make a program unnecessarily complicated, so it should be used sparingly.
Compile and run this program, even though it has no output, to assure yourself that it actually will compile. The dot notation will be used in many other places in Ada, so you should become familiar with it.
AN ADA BLOCK
Example program ------> e_c09_p3.ada
Examine the program named e_c09_p3.ada for an example of the use of an Ada block. Just as you can define a procedure and jump to it, by calling it of course, Ada allows you to define the equivalent of a procedure and execute it as inline code. Such a section of code is called a block and is constructed by using three reserved words, declare, begin, and end, with declarations between the declare and begin, and executable statements between the begin and end. Any new types, subtypes, variables, constants, and even subprograms can be declared in the declaration part of the block and used in the executable part. The scope of the declarations begin where they are declared, and end at the end of the block.
A BLOCK IS A SINGLE STATEMENT
A block is a single statement and because it is, it can be put anywhere that it is legal to put any other executable statement. It could be used within a loop, in one branch of an if statement, or even as one of the cases of a case statement. The example program contains two such blocks, the first in lines 17 through 27, and the second in lines 34 through 47. The only real difference is that the second block is a named block, with the name Who, the use of which will be defined shortly.
Study the program and you will see that even though there are several variables defined in the block, and at least one is a repeat of a global variable, all are actually available through use of the dot notation defined during our study of the last program. In the first block, the local variables are the default variables when there is a repeated name, but in the second block, the variables can be specifically named by using the dot notation. This is possible because the block is named, the name being Who in this particular case. The name is mentioned just prior to the block followed by a colon, and the name is repeated following the end of the block. In this case, the name does nothing for you, but if there were two nested blocks, either or both could be named, and you would be able to select which variable you were interested in. There is no limit to the number of blocks that can be nested.
Note that the name used for a block is not a label to which you can jump in order to execute a goto statement. The name is used only to name the block.
If no declarations are needed, you can declare a block without the reserved word declare, using only the execution block delimiters begin and end. Without the declaration part, there is little reason to declare the block until we come to the topic of exception handling where it will be extremely useful to have this capability. Compile and execute this program and study the results. Be sure you understand where each of the displayed values come from.
Examine section 5.6 of the Ada 95 Reference Manual (ARM) to gain a little more experience in working with the ARM. You may be quite surprised at the brevity of this section about the block.
WHAT ARE AUTOMATIC VARIABLES?
Example program ------> e_c09_p4.ada
This is a good time to discuss a very important topic that can have a significant effect on how you write some of your programs in the future. The topic is automatic variables, what they are and what they do for you. The best way to define them is to examine another program, and the program named e_c09_p4.ada is written just to illustrate this point.
The program is actually very simple since it is merely one big loop in which the variable Index covers the range from 1 through 10. Each time we pass through the loop, the block in lines 16 through 26 is executed and contains another loop to output some integer type data. Take careful notice of the constants and the way they are used, and you will see something that is a little strange. Each time we enter the block, we enter with a larger value for the loop variable, in this case named Index, and therefore the constants are different each time through the block. During each pass, however, they are constant, and will be treated as such. This behavior is perfectly normal and will be clearly understood when we define an automatic variable.
Prior to entering the block, the two constants, and the variable named Count_Stuff do not exist. When the block is entered, the constants are generated by the system, initialized to their constant values, and available for use within the block. Since the constants are generated each time the block is entered, it is possible to initialize them to a different value each time, which is exactly what is being done here. The process of assigning the constants their values is called elaboration.
The variable is also generated, and made available for use within the block where it is assigned a nonsense value for illustrative purposes, then never used. When program control leaves the block, in this case dropping out the bottom, the two constants and the variable disappear from existence entirely and are no longer available for use anywhere in the program. They are said to have a limited lifetime, their lifetime being from the time they are elaborated until we leave the block in which they are declared. The scope and lifetime of a variable or constant is therefore very important for you to understand.
It should be clear to you that the outer loop variable, Index, is visible from lines 12 through 29 except for lines 23 through 25. Within the region of lines 23 through 25, the outer loop variable cannot be used, even by using the expanded naming convention (i.e. - the dot notation), because there is no name for the outer loop. Naming the outer loop would make the outer loop variable available.
WHERE ELSE IS THIS USED?
This concept of the automatic variable is very important because it is used in so many places throughout an Ada program. It is used in four different places in the present example program, once in the block, as we have just mentioned, once in the main program itself, where the four variables with animal names are automatically generated, and twice in the for loops. The variables named Index in each of the for loops are automatic variables that are generated when the loop is entered, and discarded when the loop is completed. As you can see, there is a very good reason why the loop control variable is not available after you leave the loop.
Each time you call a procedure or function, the formal parameters are generated, as are the defined variables and constants. The process of variable generation and constant initialization is called elaboration. They are then used within the subprograms, and discarded when the procedure or function is completed and control is returned to the calling program.
Since the main program is itself a procedure, its variables are handled the same way.
THE STACK STORES THE AUTOMATIC ENTITIES
The generated constants and variables are stored on an internal stack, the definition of which is beyond the scope of this tutorial. If you understand what a stack is, it should be clear to you how the system can generate items, place them on the stack, use them, and discard them. Also, due to the nature of a stack, it should be clear to you how additional variables can be placed on the stack as calls are made to more deeply nested procedures and functions. Finally, it is only because of this use of automatic variables that recursive subprograms can be used. Ada requires that all subprograms be re-entrant and use of the stack makes this possible.
If the last paragraph is too technical for you, don't worry about it, because it is only mentioned for general information, and is not needed to understand Ada programming.
Compile and run e_c09_p4.ada and study the output. Be sure you understand the concept of the automatic variable because some of the advanced programming techniques in Ada will require this knowledge.
Return to the Table of Contents