Variable Storage Allocation --------------------------- BASIC stores different kinds of variables in different areas in memory. You only need to worry about where variables are stored if you are doing mixed- language programming or you are using one of the following BASIC statements or functions: CALL, CALLS (non-BASIC procedures) DECLARE (non-BASIC procedures) SADD SETMEM VARPTR VARSEG VARPTR$ BASIC stores variables either in an area called DGROUP or as far objects. (DGROUP is the name of the default data segment, the segment referenced when DEF SEG is used without an address.) Variables stored in DGROUP can be referenced by using near addresses or pointers. A near address consists of a single value or offset from the beginning of a segment or block of memory. Far objects are referenced by using far addresses or pointers. A far address consists of two parts: the starting address of a segment or block of memory, and the offset within the segment. Whether a variable is stored in DGROUP or as a far object depends first on whether it is a simple variable or an array. All simple variables are stored in DGROUP. Array storage is a little more complex and is slightly different between programs run as .EXE files and programs run within the QuickBASIC environment. When doing mixed-language programming, far addresses are preferred. This ensures that all generated code is independent of the BASIC implementation. In programs run as .EXE files, array variables are stored as follows: - All static arrays are stored in DGROUP and can be referenced with near addresses. - All dynamic arrays of variable-length strings are also stored in DGROUP and can also be referenced with near addresses. - All other dynamic arrays are stored as far objects and require far addresses. In programs run within the QuickBASIC environment, array variable storage follows these rules: - All static arrays in COMMON are stored in DGROUP and can be referenced with near addresses. - All arrays of variable-length strings are also stored in DGROUP and can also be referenced with near addresses. - All other arrays are stored as far objects and require far addresses. Because BASIC attempts to make the most efficient use of memory possible, several different things may cause variables to move in memory: - A reference to a string literal or expression - A DEF FN or FUNCTION invocation - The use of a BASIC string or memory-related function Because BASIC variables may move, use the results of a VARPTR, VARSEG, VARPTR$, or SADD function call immediately after the function call. Scoping Rules ------------- The following list summarizes BASIC's scope rules: - A variable declared in a DIM, REDIM, or COMMON statement with the SHARED attribute is a global variable to a module. Any SUB or FUNCTION procedure within the module can refer to the variable. - A symbolic constant is global if it is declared in a CONST statement in the module-level code. Symbolic constants declared in a SUB or FUNCTION are local. - A variable is a local variable if it appears in a procedure and is not declared as a global variable. You can use the name of a global variable as a local variable in a procedure by declaring it in the procedure with the STATIC statement or by using it as a formal parameter. - The SHARED statement lets you share a variable with the module-level code and other procedures with equivalent SHARED statements without making the variable a global variable. - All variables in a DEF FN function are part of the module-level code unless they are explicitly made local in a STATIC statement or are formal parameters. Scope of Variables and Constants -------------------------------- Any time a variable appears in program text, BASIC follows a set of rules to determine which object is referred to by the variable. These rules describe a variable's scope--the range of statements over which the variable is defined. You may think of variables and constants as having one of two scopes: global or local. - Global variables, once declared, may be used anywhere in a module to refer to some single object. - Local variables are local to some part of the module (that is, the module-level code or one of the procedures). In addition, variables can be shared in such a way that they aren't quite global, nor are they completely local. This is done by using the SHARED statement. Global Variables and Constants ------------------------------ Both variables and symbolic constants can be global in BASIC programs. A global variable or global symbolic constant is defined for the entire module. - A symbolic constant is a global constant if it is declared in the module-level code using a CONST statement. - For a variable, the only way to make it global is to declare it in the module-level code with the SHARED attribute in a DIM, REDIM, or COMMON statement. For example, the following program fragment makes TabStops a global variable. DIM SHARED TabStops(MAXLINE) . . . SUB SetTabPos STATIC . . . END SUB FUNCTION ThisIsATab(LastColumn AS INTEGER) STATIC . . . The SHARED statement (as opposed to the SHARED attribute, used above) allows particular procedures to share variables with the module-level code. This is not the same as making the variable global. It is only global for the module-level code and all procedures with SHARED statements for the variable. Local Variables and Constants ----------------------------- A local variable or constant exists only within a procedure or the module- level code. If the name of a local variable is used in another procedure in a module, the name represents a different variable and refers to a different object. It is simplest to think of a local variable as any variable that isn't global. Any variable that appears in module-level code or in a procedure is local if it isn't declared in a DIM, REDIM, or COMMON statement with the SHARED attribute. Even if a variable appears in one of these statements, you may still use a local variable of the same name in a procedure by declaring the variable in a STATIC statement. Any symbolic constant declared inside a SUB or FUNCTION procedure is a local constant. For example, in the following fragment, ENDOFLIST is a local symbolic constant that exists only in the function FindElement: FUNCTION FindElement(X()) CONST ENDOFLIST = -32767 . . . END FUNCTION Note: The STATIC statement not only declares a variable to be local, it also directs the compiler to save the value of the variable between procedure calls. Do not use STATIC statements in recursive procedures if you do not want a variable's value saved between calls. Sharing Variables ----------------- You can share variables among parts of a module without making the variables global by using the SHARED statement. For example, to share TabStops without making it a global variable, you would add SHARED statements to the particular procedures you want to share the variable: DIM TabStops(MAXLINE) . . . SUB SetTabPos STATIC SHARED TabStops() . . . END SUB FUNCTION ThisIsATab(LastColumn AS INTEGER) STATIC SHARED TabStops() . . . END FUNCTION The SHARED statements indicate that the name TabStops in both procedures refers to the same variable defined at the module level. Scoping rules: DEF FN Functions ------------------------------- The DEF FN is an exception to the BASIC scope rules. Every variable in a DEF FN function that isn't in its parameter list is part of the module- level code. In order to make a variable local to a DEF FN, you must declare the variable in a STATIC statement. The STATIC statement in the following DEF FN function makes the variable I local: CONST NO = 0, YES = NOT NO DEF FNIsThereAZ (A$) STATIC I FOR I = 1 TO LEN(A$) IF UCASE$(MID$(A$, I, 1)) = "Z" THEN FNIsThereAZ = YES EXIT DEF END IF NEXT I FNIsThereAZ = NO END DEF Remember, as a general rule, a FUNCTION is preferred over a DEF FN because of its improved portability, increased modularity, and more structured programming style. Static and Dynamic Arrays ------------------------- You can get better control of your program's use of memory by controlling when storage is set aside for arrays. Storage for arrays can be set aside when the program is compiled or when the program is running. - Arrays given storage when the program is compiled are static arrays. - Dynamic arrays have storage set aside when the program is run. The storage taken by dynamic arrays can be eliminated when it is needed in order to free memory for other uses. How an array is declared can determine whether the array is static or dynamic. - Arrays dimensioned with constant subscripts, or arrays that are implicitly dimensioned, are static arrays. - Arrays dimensioned with variable subscripts, or that are first declared in a COMMON statement, are dynamic arrays. - In a SUB or FUNCTION not declared STATIC, all arrays are dynamic. You can also use the $STATIC and $DYNAMIC metacommands to control how array storage is allocated. However, the $STATIC metacommand cannot force arrays to be static in a procedure not declared STATIC; in such a procedure all arrays are dynamic. Automatic and Static Variables ------------------------------ BASIC procedures can use both automatic and static variables. - Automatic variables are initialized at the start of each call to the FUNCTION or SUB. - Static variables retain values between calls. You can control whether the default is automatic or static by using or omitting the STATIC keyword in the SUB or FUNCTION statement. - If you omit STATIC, then the default for variables is automatic. - When you use STATIC, the default for all variables in the procedure is static: the values of the variables are saved between procedure calls. You can make selected variables in a procedure STATIC by making the default automatic (omitting STATIC from the SUB or FUNCTION statement) and using the STATIC statement. Type Conversions ---------------- When necessary, BASIC converts a numeric constant from one type to another, according to the following rules: - If a numeric constant of one type is set equal to a numeric variable of a different type, the numeric constant is stored as the type declared in the variable name, as in the following example: A% = 23.42 PRINT A% Output: 23 If a string variable is set equal to a numeric value, or vice versa, an error message is generated that reads: "Type Mismatch." - During expression evaluation, the operands in an arithmetic or relational operation are converted to the same degree of precision, that of the most precise operand, as each operation is performed. Also, the result of an arithmetic operation is returned to the final degree of precision, as in the following example: X% = 2 : Y! = 1.5 : Z# = 100 A! = X% / Y! PRINT A! * Z# Output: 133.3333373069763 Although the preceding result is displayed in double precision (because of the double-precision variable Z#), it has only single-precision accuracy, because the assignment to A! forced the result of X% / Y! to be reduced to single-precision accuracy. This explains the nonsignificant digits (73069763) after the fifth decimal place. Contrast this with the output from the following example in which the intermediate result of X% / Y! is retained in double precision: X% = 2 : Y# = 1.5 : Z# = 100 PRINT X% / Y# * Z# Output: 133.3333333333333 - Logical operators such as AND and NOT convert their operands to long integers if necessary. Operands must be in the range -2,147,483,648 to +2,147,483,647 or an "Overflow" error message is generated. - When a floating-point value is converted to an integer, the fractional portion is rounded, as in this example: Total% = 55.88 PRINT Total% Output: 56