We are already discussed variables in module 0012. In this module, we will talk about local variables.
A local variable is visible only from within the subroutine that contains it. Because of this property, multiple subroutines can contain local variables of the same name.
This property may not seem very important for now. However, when you need to handle programs with 100s or 1000s of thousands of lines, the last thing you want to worry is whether a variable is shared between two subroutines.
In terms of notation, we can define local variables (in pseudocode) just by saying “local varname”, where varname should be replaced by the name of an actual variable.
Here is a simple example:
Because local variables are local to to each subroutine, the output of this program is “xxxx”. However, if the variable “i” is not local to each subroutine, then what will the program output?
Tracing a program with local variable is not too different from a program with subroutines. The main difference is that instead of just creating a column for the return line number, we also create a column for the local variable(s). The trace of 1 is in table 1.
A | B | C | D | E | F |
|
line # | comments | annotation |
||||
19 | retline# | i | the invocation of sub2 and the allocation of columns |
|||
19 | ? | note how a local variable always starts with an unknown value |
||||
12 | 0 | local variable is initialized |
||||
13 | is true | refers to column D, the right-most column with a label of the name of the referenced variable |
||||
14 | retline# | i | allocate columns for the invocation of sub1 |
|||
15 | ? | note there is another column allocated with a local |
||||
3 | 0 | this refers to the rightmost column labeled |
||||
4 | is true | this refers to column F, the rightmost column in use that is labeled |
||||
5 |
| |||||
6 | 1 | |||||
4 | is true |
|
||||
5 |
|
|||||
6 | 2 |
|
||||
4 | is false |
|
||||
8 | retline# | i | retrieve return line number, deallocate columns allocated to the returning subroutine |
|||
15 | 1 | refers to the rightmost column in use that is labeled |
||||
13 | is true |
|
||||
14 | retline# | i | columns are reallocated for the second invocation of sub1 |
|||
15 | ? | note how is unknown again |
||||
3 | 0 |
|
||||
4 | is true |
|
||||
5 |
|
|||||
6 | 1 |
|
||||
4 | is true |
|
||||
5 |
|
|||||
6 | 2 |
|
||||
4 | is false |
|
||||
8 | retline# | i | deallocate columns of this invocation to use retline# to continue execution in caller |
|||
15 | 2 |
|
||||
13 | is false |
|
||||
17 | retline# | i | deallocate columns of invoking sub2 and use retline# to continue execution in caller |
|||
20 | end of execution |
Note that local variables do not start with any particular value. A local variable must be initialized before it is read.
A parameter is a method to pass data to a subroutine. A subroutine may include parameters. When such a subroutine is invoked, each parameter must be “filled” by an argument. An argument is supplied by the invoking code. There are two types of links between an argument and its parameters.
One type of parameter is called “pass by value” parameters. Such a parameter is almost like a local variable, except the initial value of such a local variable is specified by the matching argument.
Other than that, a by-value parameter behaves much like a local variable. It can only be seen inside the subroutine that defines it, it is allocated storage only when a subroutine is invoked, and it loses storage as soon as the subroutine returns.
The notation in our pseudocode defines a parameter by the keywords “byval param-name”.
Let us modify algorithm 1 to use parameters in sub1. We will eliminate sub2 and just invoke sub1 multiple times from the “main program”.
In this example, we use parameter in sub1 to specify how many “x” to print. Note that we use to specify the end value of in the loop.
Note that when we invoke sub1, we need to specify what value specifies the initial value of a parameters. This is why we have the notation on line 11 to specify that parameter should start with an initial value of 2. The use of the right arrow () indicates that information is flowing only one way (to parameter ).
Note that the left hand side of the symbol is specified by the invoking code, whereas the right hand side of the symbol specifies a parameter of the called subroutine.
To trace a subroutine with a parameter is not much different from tracing a subroutine with a local variable. The main difference is that a by-value parameter starts with a value specified by the invoking statement, whereas a local variable does not start with any particular value.
The trace of algorithm 2 is listed in table 2.
line # | comments | annotation |
|||
11 | retline# | n | i | invocation of sub1 allocates columns for retline#, the parameters and local variables of the subroutine |
|
12 | 2 | ? | the initial value of a by value parameter comes from the invoke statement, but a local variable starts with an unknown value |
||
4 | 0 | is now initialized by sub1 to 0 |
|||
5 | is true | comparing a local variable to a parameter |
|||
6 | print “x” |
||||
7 | 1 | increment |
|||
5 | is true |
|
|||
6 | print “x” |
||||
7 | 2 | increment |
|||
5 | is false |
|
|||
9 | retline# | n | i | deallocate all the columns allocated to this invocation, but use the retline# column to know which line to continue execution in the caller |
|
12 | retline# | n | i | reallocate columns for the second invocation of sub1 |
|
13 | 1 | ? |
|
||
4 | 0 | is now initialized by sub1 to 0 |
|||
5 | is true |
|
|||
6 | print “x” |
||||
7 | 1 | increment |
|||
5 | is false |
|
|||
9 | retline# | n | i | deallocate all the columns for this invocation and use retline# to know where to continue execution |
|
13 | done |
||||
Passing parameters by reference is a little bit tricky. Instead of using the technical term “by reference”, let us use the less formal term “by alias”.
A parameter is that passed by reference (alias) is, well, an alias of something else. This means when we read from such a parameter, we are reading from something else. Similarly, when we write to such a parameter, we are writing to something else.
This make by-reference parameters useful for modifying variables that belong to someone else. In other words, it make the effect of a subroutine cross the “end define sub” boundary.
The notation of such a parameter in our pseudocode is “byref param-name”.
Let us consider a relatively simple example. We are writing a subroutine to initialize a variable to 0. Algorithm 3 is an algorithm based on this subroutine.
In this example, is a variable that is local to the main program. This means this variable is not visible from the subroutine. When we invoke init0, we need to specify that argument specifies what parameter is an alias of. The use of the double arrow () indicates that information can flow both ways. Whatever happens to happens to , but any attempt to read is actually reading .
Note that the left hand side of the symbol is specified by the invoking code, whereas the right hand side of the symbol specifies a parameter of the called subroutine.
The trace of algorithm 3 is short. However, it is deceptively simple because the concept of passing by reference is very different from the concepts of local variables and passing by value.
Table 3 is a trace of algorithm 3.
A | B | C | D | annotation |
line # | mainvar | mainvar is a global variable it exists from the beginning |
||
pre | ? | a global variable starts with an unknown value |
||
6 | retline# | x | allocate one column for retline#, and one for the parameter |
|
7 | ref to col B | note a byref parameter is a reference to a column where the referred item is |
||
3 | 0 | the assignment does not change parameter , but it changes column that the parameter refers to |
||
4 | retline# | x | now we deallocate the columns. The effect on column B, however, is persistent. |
|
7 | we are all done! |