The scope of a variable is defined as its
visibility and its lifetime. Visibility means what parts of your program
can access it. Lifetime
defines when it is created and when it is destroyed. In
PowerBASIC, there are many choices of scope to afford the maximum flexibility.
You may choose any scope which best fits the needs of your program. When
any variable is created in PowerBASIC, it is automatically initialized.
Local variables are only accessible within a single SUB, FUNCTION, METHOD, or PROPERTY. They are automatically created and initialized each time you enter the procedure. They are automatically destroyed when you exit the procedure. This is the default variable scope unless you declare otherwise.
Static variables are only accessible within a single SUB, FUNCTION, METHOD, or PROPERTY. They are initialized when your program starts, but retain their value regardless of how many times the procedure is entered and exited. They are destroyed only when the program ends.
Instance variables are accessible from any method or property in a class. Each object will have its own unique copy of them. They are created and initialized when an object is created. They are destroyed when the object is destroyed.
Threaded variables are accessible from anywhere in your program, but each thread within your program will have its own unique copy of them. They are created and initialized when a thread is created. They are destroyed when the thread ends. Threaded variables are commonly called Thread Local Storage (TLS). They serve a purpose similar to global variables, but never require synchronization since they can't be accessed across threads.
Global variables are accessible from anywhere in your program. They are initialized when your program starts and are destroyed when the program ends.
In PowerBASIC, variables have a defined precedence based upon their scope. Therefore, if two or more variables are created with the same name, the programmer can know, with certainty, which variable is being accessed when the name is referenced. For example, you might use abc%, abc#, and abc$ in the same function using default typing defined by the Type ID character. You could even create a local variable named "counters" and a global variable also named "counters".
So, when you reference a variable name in your program, which variable is actually used? Depending upon the location of the reference, the compiler chooses the variable with the smallest scope. The precedence of variable scopes is:
Local or Static
Global or Threaded
By Default, PowerBASIC first tries to find a LOCAL or STATIC. Next, an INSTANCE, and finally a GLOBAL or THREADED. It selects the first one it finds, in that sequence. Of course, you cannot use the same name for a LOCAL and a STATIC, unless you use a Type ID character to differentiate them. You can never use the same name for a GLOBAL and a THREADED, as it would be impossible to tell them apart. While this method offers the most flexibility, it can be confusing, and can lead to the creation of insidious, hard-to-find errors in your program. When you accidentally reference the wrong variable, the results can be disastrous.
If you prefer to avoid name duplication, PowerBASIC offers an optional metastatement to enforce that concept. If #UNIQUE VAR ON is enabled, PowerBASIC will require that variable names be unique. The can make your job a good deal easier, as it removes the ambiguity found with identifier reuse. There are a few exceptions to the uniqueness rule, which are designed to improve readability in your code:
Local, Static, and Parameter names may be reused in other Subs, Functions, and Methods.
Instance names may reused in other Classes.
Scalar and array names may co-exist if they are the same data type and scope.
LOCAL variables are stored on the stack
frame of the procedure, so the address will change throughout the program.
it may not be safe to rely upon a pointer to them. Likewise,
INSTANCE and THREADED variables exist only as long as the object
The stack has a defined size limit. It defaults to 1 MB, but can be expanded with the #STACK metastatement. You should use care with very large local data items, like fixed or nul-terminated strings and user defined types, as they could overflow the stack. Local dynamic strings do not pose the same problem, as they require just 4 bytes of stack space each for an identifier handle.
Similarly, each local array has an associated "array descriptor". This small table is stored on the stack frame, but the actual array data is stored in main memory. Therefore, local arrays also have a small impact on the stack frame.
Default Variable Typing