Language Elements and Reference:xpplrm

Declaration of variables Foundation

Declaring variables makes their identifiers known to the compiler and causes the compiler to reserve a place to hold their values. Variable declarations must always occur before statements within the program code of a procedure or UDF. This means that the declarations of variables appear immediately after the declaration of the procedure or UDF. Xbase++ classifies variables as either dynamic or lexical, and as memory variables or field variables. Field variables are always dynamic variables and are contained in a file. Memory variables exist in memory during runtime of a program.

Lexical variables must be declared and are always memory variables. Dynamic variables can, but do not have to be declared. It is definitely recommended to declare dynamic variables as well. This allows the compiler to create optimal program code. The following table gives an overview of the different ways to declare variables:

Keywords for the declaration of variables
Keyword Description
LOCAL Declares lexical variable of the storage class LOCAL
STATIC Declares lexical variable of the storage class STATIC
FIELD Declares dynamic variable as a field variable
MEMVAR Declares dynamic variable as a memory variable

These four keywords declare variable identifiers for the compiler and associate them with a specific storage class. The storage class determines the visibility and lifetime of the variables. The visibility determines the ability to access a variable within different segments of program code. The lifetime of a variable determines how long the value of the variable exists.

The two declarations FIELD (field variable) and MEMVAR (memory variable) indicate to the compiler that these identifiers name dynamic variables which exist only at runtime of a program. Field variables exist as soon as a corresponding file is opened by a DatabaseEngine and dynamic memory variables are created at runtime using the following keywords:

Keywords for the creation of dynamic variables
Keyword Description
PRIVATE Creates dynamic memory variable as a PRIVATE variable
DECLARE Synonym for PRIVATE (compatibility)
PUBLIC Creates dynamic memory variable as a PUBLIC variable

The decision of which storage class to use for a variable depends on the purpose of the variable and what happens to the value of the variable at runtime of the program. Also, visibility and lifetime of a variable must be considered.

Visibility and lifetime

This section concerns only memory variables, since field variables are saved in files that continue to exist when the program has terminated. Field variables are visible to the program as soon as the corresponding file has been opened using a DatabaseEngine (see the section "Alias operator" in the chapter "Procedures, Functions and Special Operators").

The lifetime of variables at runtime of a program depends on the storage class and follows these rules:

LOCAL

A LOCAL variable only exists as long as the function or procedure in which the variable is declared remains active. After the statement RETURN, LOCAL variables are released.

(Exception: using code blocks, LOCAL variables can be detached from the context of the declaring function. They then continue to exist within the code block. See the section "Operations using Code Blocks"in the chapter "Operators and Operations for Special Data Types")

STATIC

A STATIC variable is initialized at the start of the program and retains its value until the program ends.

PRIVATE

A PRIVATE variable exists as long as the function or procedure in which the variable is created remains active or until it is explicitly released using the command RELEASE.

PUBLIC

A PUBLIC variable exists from the point in time that it is created until the program terminates or until it is explicitly released using the command RELEASE.

Along with lifetime, the visibility of variables plays an important role in determining how a variable can be accessed within the program. Access to a variable within the program occurs using the identifier for the variable. Visibility is defined as follows:

LOCAL

A LOCAL variable is only visible within the function or procedure in which it has been declared.

STATIC

When STATIC variables are declared at the very beginning of a program file prior to the first declaration of a function or procedure, they are visible within the entire file (file-wide visibility). STATIC variables declared within a function or procedure are only visible within this function or procedure. They have the same visibility as LOCAL variables.

PRIVATE

A PRIVATE variable is visible from the point of time it is declared. They also remain visible in all called routines (downward in the call stack). If a called routine creates a new PRIVATE variable with the same identifier, the new variable supersedes the previously existing variable and hides the previous variable from view. When several PRIVATE variables in different subroutines have the same identifier, only the last PRIVATE variable created is visible.

PUBLIC

A PUBLIC variable is visible throughout all program code unless a PRIVATE variable with the same identifier is created. In this case, the PUBLIC variable is superseded and hidden by the PRIVATE variable and only becomes visible again when the PRIVATE variable is released.

The declarations LOCAL and STATIC determine the visibility of lexical variables at compile time. The declaration MEMVAR is used to declare dynamic memory variables and does not also determine their visibility. This occurs only when dynamic memory variables are created with PRIVATE or PUBLIC. MEMVAR essentially tells the compiler to use an identifier as a place holder for a memory variable rather than for a field variable.

Lexical memory variables - code example

Within functions and procedures, identifiers for lexical variables may be declared only once. The declaration must always occur before the first statement, expression or command. STATIC variables may be declared prior to the first declaration of a function or procedure in a file. They then have file-wide visibility.

STATIC snCount:= 1000           // file-wide visible STATIC variable 
                                // declared and initialized 

************** 
PROCEDURE Main 
   LOCAL i, nSum := 0           // 2 LOCAL variables only visible 
                                // in this procedure 

   ElapsedSecs(Seconds())       // start counter for seconds 
   FOR i:=1 TO snCount 
      nSum += i                 // calculate a sum 
   NEXT 

   ? ElapsedSecs()              // display time taken to sum 
                                // the numbers from 1 to 1000 
RETURN 

******************************* 
FUNCTION ElapsedSecs(nNewStart) // parameter as a LOCAL variable 
   STATIC snStart               // STATIC variable only visible 
                                // in function 
   IF nNewStart<>NIL 
      snStart := nNewStart      // resets count 
   ENDIF 

RETURN (Seconds() - snStart)    // difference between two calls 

This code example shows various ways to declare lexical variables. Several variables can be declared and optionally initialized at once if the identifiers are separated using commas. Initializing as part of the declaration is only possible using the inline assignment operator. The variable then has the value assigned to it as soon as it is declared. When STATIC variables are to be initialized as part of their declaration, the value must be available at compile time. LOCAL variables, however, allow the initializing value to be determined at runtime. This means that STATIC variables can be initialized only with literal values and not with return values of functions:

STATIC scStartTime := Time()     // invalid initialization 
 LOCAL  cStartTime := Time()     // valid initialization 
STATIC scStartTime := "00:00:00" // valid initialization 

The program example above shows the special characteristic of STATIC variables. The variable snCount is not declared in the procedure Main, but is specified as a STATIC variable visible file-wide prior to the declaration of the procedure Main. Within Main the variable snCount can be accessed.

In the function ElapsedSecs(), a STATIC variable is declared which is only visible within this function. The variable snStart is initialized with the parameter nNewStart the first time the function is called. It then retains this value so that other calls to the function return the time difference between the first call and the current call.

Lexical variables of the storage class LOCAL and STATIC are generally preferred over dynamic memory variables. The main reason is greater access speed and greater reliability in knowing which variable identifiers are accessed. The disadvantage of lexical variables is that they cannot be referenced within a macro expression cannot be saved in a memory variable file. Both of these operations are possible only with dynamic memory variables (see the section"Macro operator"in the chapter "Procedures, Functions and Special Operators" as well as the command SAVE in the Xbase++ language reference).

Dynamic memory variables - code example

Xbase++ is one of the few program languages which allows dynamic memory variables as well as lexical memory variables. When variables are not declared within a program, they are created as dynamic memory variables of the storage class PRIVATE. The following example shows valid program code using two undeclared variables:

PROCEDURE Main 

   dDate := Date()               // undeclared memory 
   cDay  := CDow(dDate)          // variable 

   ? "Today is "+ cDay + ", " + dDate 
                                 // result: 
                                 // Today is Tuesday, 12/06/94 
RETURN 

The two variables dDate and cDay are created at runtime of the program. Since values are assigned to them, they are also initialized. Accessing undeclared memory variables which are not initialized leads to runtime errors. While undeclared variables are allowed, well written programs do not contain undeclared variables for reasons including program reliability. The Xbase++ compiler offers an option to output a warning message on the screen when it encounters an undeclared variable.

For the compiler to generate optimal code, the program example above must appear as follows:

PROCEDURE Main 
   MEMVAR dDate, cDay            // declare dynamic 
                                 // memory variable 
   PRIVATE dDate := Date()       // create and initialize 
   PRIVATE cDay  := CDow(dDate)  // PRIVATE variables 

   ? "Today is "+ cDay + ", " + dDate 
RETURN 

The two dynamic variables are now declared as memory variables by MEMVAR and are associated with the storage class PRIVATE by the keyword PRIVATE. The declaration using MEMVAR is limited to the procedure Main in the example . The declaration MEMVAR, just like STATIC, can appear before the first declaration of a procedure or function at the very beginning of a program file. When it appears here, the declaration has file-wide visibility. The following example shows this process and demonstrates characteristics of PRIVATE and PUBLIC variables:

MEMVAR varA, varB, varC, varD, varE // file-wide MEMVAR declaration 

************** 
PROCEDURE Main 
   PRIVATE varA := "A", varB := "B", varC := "C" 

   ? varA, varB, varC               // result: A B C 
   Proc1() 
   ? varA, varB, varC, varD, varE   // result: A 2 C 4 e 

RETURN 

*************** 
PROCEDURE Proc1 
   PRIVATE varA := "a"            // new PRIVATE variable supersedes 
                                  // old PRIVATE variable varA 
   PUBLIC  varD := "d", varE := "e" // new PUBLIC variables 

   ? varA, varB, varC, varD, varE   // result: a B C d e 
   Proc2() 
   ? varA, varB, varC, varD, varE   // result: 1 2 C 4 e 

RETURN 

*************** 
PROCEDURE Proc2 
   PRIVATE varE := "5"              // new PRIVATE variable covers 
                                    // existing PUBLIC variable varE 

   varA := "1"                      // PRIVATE varA created in Proc1 
   varB := "2"                      // PRIVATE varB created in Main 
   varD := "4"                      // PUBLIC varD created in Proc1 

   ? varA, varB, varC, varD, varE   // result: 1 2 C 4 5 

RETURN 

The example program shows the ease and danger of using dynamic memory variables. They are easier to use because they need not be declared but they can cause serious programming errors due to their visibility. The example should be read carefully in order to thoroughly understand the rules about lifetime and visibility of dynamically scoped memory variables.

All variables varA to varE are declared at the start as MEMVAR and are displayed on the screen in each of the three procedures Main, Proc1 and Proc2. In Main and Proc1, the display occurs before and after calling a subroutine. The variable varC is created in Main and is later shown to be unchanged. The PRIVATE variable varA gets a new value assigned to it in each procedure, but in Proc1 a new variable with this identifier is created and it is this variable that gets the new value in Proc2. varA in the procedure Main remains unchanged. The variable varB is visible down the call stack. It is unchanged in Proc1 and has a new value assigned in Proc2. It retains this value in the procedure Main after completion of the subroutines.

The PUBLIC variables are first created in Proc1, but remain after this procedure returns. The PRIVATE variable varA is also recreated in Proc1. But this variable does not remain and is released after termination of Proc1. This allows the variable varA created in Main to again be visible. In Proc2 a PRIVATE variable varE is created which supersedes the PUBLIC variable of the same name. After termination of the procedure Proc2, the PUBLIC variable varE becomes visible again.

The example illustrates the ability of dynamic variables to obscure existing memory variables having the same identifier so that the existing variables are no longer visible. This process can cause very serious programming errors. Dynamic memory variables should only be used when they present a genuine advantage over lexical memory variables in a specific situation.

It is suggested that all values which must have global visibility be kept in PUBLIC variables (example: system state variables). Dynamic memory variables have an advantage over lexical variables in that they can be referenced in macro expressions (see the section "Macro operator" in the chapter "Functions, Procedures and Special Operators"). The essential advantage of dynamic memory variables, however, is that they can be saved in a file at runtime using the command SAVE and be reloaded later into the program using RESTORE. This allows arrays and code blocks, which cannot generally be stored by a DatabaseEngine, to be saved to disk.

Field variables - code example

Declaration of a dynamic variable using FIELD assures that a variable in the visibility scope of the declaration always references a field variable and not a memory variable. The declaration FIELD, like STATIC and MEMVAR, is valid for the entire file if it appears before the first declaration of a function or procedure at the very beginning of a program file. If it appears after the keyword FUNCTION or PROCEDURE, it is valid only within the context of that function or procedure.

Using FIELD, conflicts can occur when a memory variable and a field variable have the same identifier. An example of such a conflict is shown in the following program lines:

************** 
PROCEDURE Main 
   MEMVAR Name 

   PRIVATE Name := "Alaska Software" 

   ? Name                          // result: Alaska Software 
   Proc1() 

RETURN 

*************** 
PROCEDURE Proc1 
   FIELD Name                      // declare field variable 

   USE Address                     // open file via DatabaseEngine 
   ? Name                          // result: Miller 
   ? MEMVAR->Name                  // result: Alaska Software 
   USE 

RETURN 

Such conflicts can generally be avoided by careful selection of variable identifiers or by using the alias operator -> (see the section "Alias Operator" in the chapter "Procedures, Functions and Special Operators").

When an undeclared and uninitialized variable appears in a program at runtime, by default an attempt is made to access a field variable with this identifier. Note the following example:

PROCEDURE Main 

   USE Address             // open file via DatabaseEngine 
   ? LName                 // result: Miller 
   CLOSE Address           // close file 
   ? LName                 // runtime error 

RETURN 

Typically, field variables are generally not declared but memory variables are always declared. Field variables are always dynamic variables and when there are ambiguous variables at runtime, the field variable is accessed (unless the compiler toggle /V is used to indicate that the memory variable is accessed in these cases). Safer and more readable access to field variables generally occurs using the alias operator provided especially for programming with field variables.

Feedback

If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.