5.5 Local Variables


5.5 Local Variables

HLA procedures, like procedures and functions in most high level languages, let you declare local variables. Local variables are generally accessible only within the procedure, they are not accessible by the code that calls the procedure. Local variable declarations are identical to variable declarations in your main program except, of course, you declare the variables in the procedure's declaration section rather than the main program's declaration section. Actually, you may declare anything in the procedure's declaration section that is legal in the main program's declaration section, including constants, types, and even other procedures.[3] In this section, however, we'll concentrate on local variables.

Local variables have two important attributes that differentiate them from the variables in your main program (i.e., global variables): lexical scope and lifetime. Lexical scope, or just scope, determines where an identifier is usable in your program. Lifetime determines when a variable has memory associated with it and is capable of storing data. Because these two concepts differentiate local and global variables, it is wise to spend some time discussing these two attributes.

Perhaps the best place to start when discussing the scope and lifetimes of local variables is with the scope and lifetimes of global variables — those variables you declare in your main program. Until now, the only rule you've had to follow concerning the declaration of your variables has been "you must declare all variables that you use in your programs." The position of the HLA declaration section with respect to the program statements automatically enforces the other major rule which is "you must declare all variables before their first use." With the introduction of procedures, it is now possible to violate this rule because (1) procedures may access global variables, and (2) procedure declarations may appear anywhere in a declaration section, even before some variable declarations. The program in Listing 5-5 demonstrates this source code organization.

Listing 5-5: Demonstration of Global Scope.

start example
 program demoGlobalScope; #include( "stdlib.hhf" ); static      AccessibleInProc: char;      procedure aProc;      begin aProc;           mov( 'a', AccessibleInProc );      end aProc; static      InaccessibleInProc: char; begin demoGlobalScope;      mov( 'b', InaccessibleInProc );      aProc();      stdout.put      (           "AccessibleInProc   = '", AccessibleInProc,   "'" nl           "InaccessibleInProc = '", InaccessibleInProc, "'" nl      ); end demoGlobalScope; 
end example

This example demonstrates that a procedure can access global variables in the main program as long as you declare those global variables before the procedure. In this example, the aProc procedure cannot access the InaccessibleInProc variable because its declaration appears after the procedure declaration. However, aProc may reference AccessibleInProc because its declaration appears before the aProc procedure.

A procedure can access any static, storage, or readonly object exactly the same way the main program accesses such variables — by simply referencing the name. Although a procedure may access global var objects, a different syntax is necessary, and you need to learn a little more before you will understand the purpose of the additional syntax (for more details, please consult the HLA Reference Manual).

Accessing global objects is convenient and easy. Unfortunately, as you've probably learned when studying high level language programming, accessing global objects makes your programs harder to read, understand, and maintain. Like most introductory programming texts, this book discourages the use of global variables within procedures. Accessing global variables within a procedure is sometimes the best solution to a given problem. However, such (legitimate) access typically occurs only in advanced programs involving multiple threads of execution or in other complex systems. Because it is unlikely you would be writing such code at this point, it is equally unlikely that you will absolutely need to access global variables in your procedures so you should carefully consider your options before accessing global variables within your procedures.[4]

Declaring local variables in your procedures is very easy; you use the same declaration sections as the main program: static, readonly, storage, and var. The same rules and syntax for the declaration sections and the access of variables you declare in these sections applies in your procedure. The example code in Listing 5-6 demonstrates the declaration of a local variable.

Listing 5-6: Example of a Local Variable in a Procedure.

start example
 program demoLocalVars; #include( "stdlib.hhf" );      // Simple procedure that displays 0..9 using      // a local variable as a loop control variable.      procedure CntTo10;      var           i: int32;      begin CntTo10;           for( mov( 0, i ); i < 10; inc( i )) do                stdout.put( "i=", i, nl );           endfor;      end CntTo10; begin demoLocalVars;      CntTo10(); end demoLocalVars; 
end example

Local variables you declare in a procedure are accessible only within that procedure.[5] Therefore, the variable i in procedure CntTo10 in Listing 5-6 is not accessible in the main program.

HLA relaxes, somewhat, the rule that identifiers must be unique in a program for local variables. In an HLA program, all identifiers must be unique within a given scope. Therefore, all global names must be unique with respect to one another. Similarly, all local variables within a given procedure must have unique names but only with respect to other local symbols in that same procedure. In particular, a local name may be the same as a global name. When this occurs, HLA creates two separate variables. Within the scope of the procedure any reference to the common name accesses the local variable; outside that procedure, any reference to the common name references the global identifier. Although the quality of the resultant code is questionable, it is perfectly legal to have a global identifier named MyVar with the same local name in two or more different procedures. The procedures each have their own local variant of the object which is independent of MyVar in the main program. Listing 5-7 provides an example of an HLA program that demonstrates this feature.

Listing 5-7: Local Variables Need Not Have Globally Unique Names.

start example
 program demoLocalVars2; #include( "stdlib.hhf" ); static      i: uns32 := 10;      j: uns32 := 20;      // The following procedure declares "i" and "j"      // as local variables, so it does not have access      // to the global variables by the same name.      procedure First;      var           i: int32;           j:uns32;      begin First;           mov( 10, j );           for( mov( 0, i ); i < 10; inc( i )) do                stdout.put( "i=", i," j=", j, nl );                dec( j );           endfor;      end First;      // This procedure declares only an "i" variable.      // It cannot access the value of the global "i"      // variable but it can access the value of the      // global "j" object because it does not provide      // a local variant of "j".      procedure Second;      var           i:uns32;      begin Second;           mov( 10, j );           for( mov( 0, i ); i < 10; inc( i )) do                stdout.put( "i=", i," j=", j, nl );                dec( j );           endfor;      end Second; begin demoLocalVars2;      First();      Second();      // Because the calls to First and Second have not      // modified variable "i", the following statement      // should print "i=10". However, because the Second      // procedure manipulated global variable "j", this      // code will print "j=0" rather than "j=20".      stdout.put( "i=", i, " j=", j, nl ); end demoLocalVars2; 
end example

There are good and bad points to be made about reusing global names within a procedure. On the one hand, there is the potential for confusion. If you use a name like ProfitsThisYear as a global symbol and you reuse that name within a procedure, someone reading the procedure might think that the procedure refers to the global symbol rather than the local symbol. On the other hand, simple names like i, j, and k are nearly meaningless (almost everyone expects the program to use them as loop control variables or for other local uses), so reusing these names as local objects is probably a good idea. From a software engineering perspective, it is probably a good idea to keep all variables names that have a very specific meaning (like ProfitsThisYear) unique throughout your program. General names that have a nebulous meaning (like index, counter, and names like i, j, or k) will probably be okay to reuse as global variables

There is one last point to make about the scope of identifiers in an HLA program: Variables in separate procedures are separate, even if they have the same name. The First and Second procedures in Listing 5-7, for example, share the same name (i) for a local variable. However, the i in First is a completely different variable than the i in Second.

The second major attribute that differentiates (certain) local variables from global variables is lifetime. The lifetime of a variable spans from the point the program first allocates storage for a variable to the point the program deallocates the storage for that variable. Note that lifetime is a dynamic attribute (controlled at runtime), whereas scope is a static attribute (controlled at compile time). In particular, a variable can actually have several lifetimes if the program repeatedly allocates and then deallocates the storage for that variable.

Global variables always have a single lifetime that spans from the moment the main program first begins execution to the point the main program terminates. Likewise, all static objects have a single lifetime that spans the execution of the program (remember, static objects are those you declare in the static, readonly, or storage sections). This is true even within procedures. So there is no difference between the lifetime of a local static object and the lifetime of a global static object. Variables you declare in the var section, however, are a different matter. var objects use automatic storage allocation. Automatic storage allocation means that the procedure automatically allocates storage for a local variable upon entry into a procedure. Similarly, the program deallocates storage for automatic objects when the procedure returns to its caller. Therefore, the lifetime of an automatic object is from the point of the execution of the first statement in a procedure to the point it returns to its caller.

Perhaps the most important thing to note about automatic variables is that you cannot expect them to maintain their values between calls to the procedure. Once the procedure returns to its caller, the storage for the automatic variable is lost and, therefore, the value is lost as well. Thus, you must always assume that a local var object is uninitialized upon entry into a procedure; even if you know you've called the procedure before and the previous procedure invocation initialized that variable. Whatever value the last call stored into the variable was lost when the procedure returned to its caller. If you need to maintain the value of a variable between calls to a procedure, you should use one of the static variable declaration types.

Given that automatic variables cannot maintain their values across procedure calls, you might wonder why you would want to use them at all. However, there are several benefits to automatic variables that static variables do not have. The biggest disadvantage to static variables is that they consume memory even when the (only) procedure that references them is not running. Automatic variables, on the other hand, only consume storage while their associated procedure is executing. Upon return, the procedure returns any automatic storage it allocated back to the system for reuse by other procedures. You'll see some additional advantages to automatic variables later in this chapter.

[3]Strictly speaking, this is not true. You may not declare external objects within a procedure. External objects are the subject of a later section in this chapter.

[4]Note that this argument against accessing global variables does not apply to other global symbols. It is perfectly reasonable to access global constants, types, procedures, and other objects in your programs.

[5]Strictly speaking, this is not true. The chapter on advanced procedures will present an exception.




The Art of Assembly Language
The Art of Assembly Language
ISBN: 1593272073
EAN: 2147483647
Year: 2005
Pages: 246
Authors: Randall Hyde

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net