Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 12

Storage Classes

Introduction
Every variable has a storage class that determines how and when storage is allocated for it. A variable's storage class specifically determines whether storage will be allocated for it at compile time or dynamically during program execution. Storage classes are declared for all variables, except those used as parameters, using one of the following attributes: AUTOMATIC STATIC BASED CONTROLLED DEFINED

If no storage class is specified for a variable declared in a procedure, other than a parameter variable, the default is AUTOMATIC. If a variable is declared outside the scope of all external procedures in a source module, the default storage class is STATIC. A parameter shares storage with its argument. As such, the storage class is ultimately determined by that of the associated argument. For convenience, a parameter may be considered to have a distinct "parameter" storage class. A variable's storage size is determined by its extents. A string variable's extent is its declared length. An array variable's extents are determined from the number of its dimensions and the range of its subscript bounds. The extents of both types of variables are evaluated when storage is allocated for the variable. The permitted forms for extents differ for each storage class and are described during the discussions of each storage class.

Automatic Storage
Storage for an automatic variable is allocated with each activation of the procedure or the BEGIN block in which the variable is declared. Storage is allocated within the stack frame that represents that block activation. If a block is activated recursively, each of its stack frames contains a distinct instance of all automatic variables declared in that block. When a block activation ends, its stack frame is popped from the stack, thereby freeing the storage of all automatic variables allocated within it. This phenomenon normally occurs when a procedure returns to its caller or when a BEGIN block executes its END statement; however, it also occurs when a GOTO statement transfers control back to a previous block activation. In the latter case, all block activations between the one to which control is transferred and the one in which the GOTO was executed are ended. Their stack frames are popped from the stack, releasing the storage for all of their automatic variables. The INITIAL attribute can be specified in the declaration of an automatic variable to initialize the variable. Extents of automatic character string and array variables may be specified as integer valued expressions, provided these do not contain references to other non-static variables declared in the same block. Extent expressions for these automatic variables are evaluated each time the containing block is activated. The extent expression values are saved in the stack frame and effectively fix the size of the automatic variable for that block activation. Subsequent assignment to a variable used as an extent does not affect the size of the associated automatic variable. For example:

DECLARE STRING_SIZE FIXED; . . . COPY: BEGIN; DECLARE TEXT CHARACTER(STRING_SIZE); When the BEGIN block in this example is activated, the extent of TEXT is evaluated. The variable is allocated storage depending on the value of STRING_SIZE, which must be a valid integer value. For example: DECLARE A(N) FIXED; . . . N = 10; /* WILL NOT AFFECT THE SIZE OF ARRAY A */ In this example, the size of A is determined upon entry to the containing block by evaluating N and storing its value in the stack frame. The assignment to N does not change the size of A. Upon entry to the block, N must have a correct value and must not be a non-static variable declared in this block. Use of the HBOUND built-in function within this block activation would return the original value of N that was saved in the stack frame. Parent topic: Storage Classes Related information INITIAL HBOUND Function

Static Storage
Storage for a static variable is allocated by the Compiler and/or linker prior to program execution. Storage remains allocated throughout program execution. Only one instance of a static variable's storage is allocated, regardless of whether the variable is declared in a recursive procedure. Values assigned to a static variable are retained between calls to the procedure in which the variable is declared. All extents specified for static character string, 8-bit area variables, and array variables must be constants. Static variables can be declared with the INTERNAL or EXTERNAL attribute. If neither attribute is specified, INTERNAL is assumed. An internal static variable is known in its containing block and in all contained blocks, except those blocks in which the same name is redeclared. This is the normal scope rule that applies to all other variables. An external static variable is known in its containing block and in all contained blocks, except those blocks in which the same name is redeclared. However, all declarations of the same name that have the EXTERNAL attribute, including those in separate modules of the program, share the same storage and must specify identical attributes, including any initial values. External static variables are known and shared by all program modules in a manner similar to the common variables of FORTRAN, except that each external static variable is shared independently of others. Parent topic: Storage Classes

Based Variables
The BASED attribute defines a template for storage that will be accessed through the use of a locator. A based variable differs from all other variables in the sense that it is a description of storage, but does not have storage of its own. Since it has no storage, it has no address. For example: DECLARE X CHARACTER(30) BASED; DECLARE P POINTER; . . . P->X = 'ABC'; In this example, assume that an address or pointer value has been assigned to P. The reference P>X is called a pointer qualified reference, and it enables assignment to the storage addressed by P as if that storage contained a character-string variable like X. A pointer value can be obtained in two primary ways: from the address of a variable's storage as calculated and returned by the ADDR built-in function, or from the address of storage obtained dynamically by the ALLOCATE statement. For example: DECLARE K FIXED BINARY(15); DECLARE X(5) FLOAT BINARY; DECLARE Y FLOAT BINARY BASED; DECLARE P POINTER; . . . P = ADDR(X(K)); P->Y = 10; In this example, P is assigned the address of X (K) , which is returned by the ADDR built-in function. The second assignment places the value 10 in the storage location given by P in a manner prescribed by the template Y, effectively assigning 10 to X (K) . A third way to obtain a pointer value is with the POINTER built-in function, which may be used to convert an area offset value to a pointer. The indiscriminate use of pointers to access the storage of other variables should be discouraged because of the possibilities of unexpected assignment side effects and because the excessive use of pointers makes a program hard to read. Please also note that no checking is performed to ensure that the based variable provides a data description compatible with the declaration of the reference storage. For instance, the statement P>Y = 10 might produce undesirable results if Y were declared as a data type incompatible with the data type of elements in X. For a discussion of storage sharing using based variables, see the section Storage Sharing. The ALLOCATE statement uses a based variable to allocate a block of storage that can later be accessed by a based variable together with the locator returned by the ALLOCATE statement. For example: DECLARE P POINTER; DECLARE X(C,N) CHAR BASED(P); N = 10;

C = 10; ALLOCATE X SET(P); . . . P -> X(10,5) = 'A'; In the preceding example, the ALLOCATE statement allocates a storage block of sufficient size to hold a two-dimensional array of 100 one-byte character values and assigns a pointer to that block of storage to P . P>X thereafter is a reference to that block of storage as an array of 100 one-byte character values. Based variables can be subscripted just like any other variable. Referring again to the previous example, the statement P> X (10,5) = ' A' assigns the character 'A' to the element in row 10 and column 5 of the array qualified by P. Since the pointer variable may also be an array, it too can be subscripted. For example: DECLARE P(3) POINTER; DECLARE X(10) FLOAT BASED; . . . ALLOCATE X SET(P(K)); In the preceding example, P (K)>X refers to the entire array of ten floating-point elements qualified by the Kth pointer element of P. Likewise, P (K) >X (J) refers to the Jth element of the floating-point array qualified by the Kth element of pointer array P. The ALLOCATE statement is the only context in which a based variable can be used without an explicit or implicit locator qualifier. In this context, the based variable is used to describe how much storage is to be allocated. Implicit locator qualification is a shorthand notation that can be used in cases where many of the references to a based variable use the same pointer. For example: DECLARE X(10) FLOAT BASED(P); DECLARE P POINTER; . . . ALLOCATE X SET(P); Because X was declared to be based on P in the previous example, unqualified references to X such as X (K) , X (5), or X are implicitly qualified by P and are equivalent to P>X (K) , P>X (5) , and P>X. Any explicit qualification, such as Q>X, may be used to temporarily override the implicit qualification and is unaffected by the specification of P in BASED (P) . The extents of a based variable may be specified as integer valued expressions. The extents are evaluated for each allocation of the variable and for each reference of the variable. The extents are not captured when storage is allocated by an ALLOCATE statement. The extent expression can reference variables declared in the same block, as long as those variables have a value before the based variable is referenced. Changing the value of a variable referenced by an extent expression changes the extent of the based variable. For example:

DECLARE X(N) CHARACTER(1) BASED; N = 10; ALLOCATE X SET(P); . . . N = M; P->X(5) = 'A'; In this example, the ALLOCATE statement allocates an array of ten elements. The value of M must be greater than or equal to five and less than or equal to ten; otherwise, the reference to P>x (5) is invalid and may cause unpredictable results. A block of storage previously allocated by an ALLOCATE statement can be freed by a FREE statement. For example: FREE P->X; Once freed, the storage can no longer be accessed, and any attempt to use a pointer such as P that points to the block produces unpredictable results. Note: The storage obtained by an ALLOCATE statement is freed only by an explicit FREE. If a program continually allocates memory without freeing it, the program may run out of space at run-time. Used with the ALLOCATE statement, based variables are powerful tools for applications in which the size of arrays or strings is not known at block entry and must be determined at run-time. Based variable allocations also supply a means for the dynamic creation of graph structure nodes (which may be linked by pointers to produce list and tree structures). See the example of a based structure in the section Block Activation and Recursion. It is desirable to restrict the extent of based variables to constant values whenever possible to avoid unexpected side effects and to improve program readability. If the extent must be variable, it is preferable to avoid the use of extent expressions when declaring based variables for the same reasons. The size of the based variable is evaluated for each reference to the variable.

Controlled Variables
The CONTROLLED attribute specifies a variable whose storage is dynamically allocated and freed in generations, independent of block boundaries. Controlled variables are allocated only when specified in an ALLOCATE statement. After being allocated, a controlled variable remains allocated until a FREE statement names the variable. A reference to a controlled variable refers to the generation of the variable created by the most recent ALLOCATE statement. The FREE statement frees the most recent generation of the controlled variable, exposing the previous generation. For example: DECLARE STACK (STACK_ITEM_SIZE) CHAR(32) CTL; DECLARE STACK_ITEM_SIZE FIXED BIN; DECLARE I FIXED BIN; /* Push a first stack item. */

STACK_ITEM_SIZE = 4; ALLOCATE STACK; DO I = 1 TO STACK_ITEM_SIZE; STACK(I) = CHAR(I) || '_1'; END; /* Push a second stack item. */ STACK_ITEM_SIZE = 3; ALLOCATE STACK; DO I = 1 TO STACK_ITEM_SIZE; STACK(I) = CHAR(I) || '_2'; END; PUT SKIP LIST ('Print second item on the stack'); PUT SKIP LIST (STACK); /* Pop the last element on the stack. */ FREE STACK;

PUT SKIP LIST ('Print first item on the stack'); PUT SKIP LIST (STACK); /* Pop the last element on the stack. The stack is empty now. */ FREE STACK; CONTROLLED variables can be declared with asterisk (*) extents, and when such a variable is allocated, its extents can be made explicit by the use of the ALLOCATE statement with specified attributes. CONTROLLED variables can also be declared with explicit extents (constant or variable), and these explicit extents can be overridden by use of the ALLOCATE statement with specified attributes. It is also possible to specify attributes with asterisk extents in the ALLOCATE statement. This causes the extents to be copied from the corresponding extents used for the previous allocation of the same CONTROLLED variable. If an asterisk is specified in the ALLOCATE statement for one dimension of a variable, it must be specified for all dimensions of that variable. If level numbers are specified, the first variable in the ALLOCATE statement must be specified with a level-num of 1 and be declared as a level-1 structure, and in this case, all members of the structure must be included in the ALLOCATE statement just as they appear in the declaration of the structure.

Any bound, length, size, or initial value specified in the ALLOCATE statement overrides the corresponding attribute specified in the declaration of the variable. A bound, length, or size can be specified in the ALLOCATE statement with an asterisk, and, in this case, the bound, length, or size is taken from the current generation of that CONTROLLED variable. If a bound, length, size, or initial value specified in the ALLOCATE statement contains a reference to the variable being allocated, the reference is to the current generation of the variable. Evaluations of expressions for bounds, lengths, sizes, and initial values during the execution of the ALLOCATE statement are done according to the normal rules for PL/I references, and must be capable of logical execution (that is, without circularity). Evaluations depending on the existence of a prior generation of a variable require that that generation has previously been allocated. Controlled variables provide an easy way to implement a stack, because only the most recent generation is available to the program. The ALLOCATE statement is equivalent to a push operation and the FREE statement is equivalent to a pop operation. The ALLOCATION built-in function is used to determine the number of generations of a controlled variable that are available. Examples DECLARE 1 A CONTROLLED, 2 B CHAR(*), 2 C CHAR(20), 2 D(2) FIXED BIN(31), 2 E(4,4) CHAR(*), 2 F FIXED BIN(31); ... ALLOCATE 1 A, 2 B CHAR(12), 2 C, 2 D(6), 2 E CHAR(3), 2 F INIT(128); ... ALLOCATE 1 A, 2 B CHAR(12), 2 C CHAR(10), 2 D(D(1)), 2 E CHAR(3), 2 F; In this example, the structure A is first allocated with member B of length 12, with array member D with dimension (6) instead of the declared (2), array member E with its declared dimensions of (4,4) and element lengths of 3, and member F with an initial value of 128. The second generation of A is allocated with member C of length only 10 and with the dimension of array member D the value contained in the first generation of A.D(1). DECLARE X(*,*) CONTROLLED; ALLOCATE X(3,7); ALLOCATE X(*,*); In the second allocation of array X, the bounds are implicitly (3,7), as derived from the previous allocation. For CONTROLLED parameters, CONTROLLED must be explicitly stated in the parameter descriptor for the ENTRY declaration. This is required by the PL/I language specification. For example, if you compile and link together the following two procedures: t.pl1 TSTENTM: PROC OPTIONS(MAIN); DCL DCL N CHAR(10) CONTROLLED;

TSTENT ENTRY(CHAR(10));

ALLOCATE N; N = 'CONTROLLED'; CALL TSTENT(N); END; t1.pl1 TSTENT: DCL N PROC(N); CHAR(10) CONTROLLED;

PUT SKIP LIST ('N = ',N); RETURN; END; Upon execution you will get: *** Condition ERROR raised *** Unhandled condition SIGSEGV at PC=004011AE N = To avoid this, add the controlled attribute to the ENTRY declaration: DCL TSTENT ENTRY(CHAR(10)CTL);

Defined Variables
A defined variable is one for which no storage is allocated. Instead, the variable shares the storage of a specified basis variable. A defined variable is declared with the DEFINED attribute, which also specifies the basis variable. Any reference to the defined variable is a reference to all or part of the storage of the basis variable. When the declaration of a variable includes the DEFINED attribute, that variable's description applies to the storage associated with the basis variable. For example: DECLARE A(5) CHARACTER(1); DECLARE X CHARACTER(5) DEFINED(A); In this example, X is a defined character-string variable of length 5. X shares storage with, and is an alternate description of, the array A. Similarly, the following statement: X = 'abcde'; assigns 'a' to A (1) , 'b' to A(2), and so on. The extents of a defined variable can be integer valued expressions. They are evaluated upon block entry and stored in the stack frame just like the extents of automatic variables. Such an extent expression must not contain a reference to any non-static variable declared in the same block.

Parameter Storage Class

A parameter shares storage with its argument. As such, the storage class is ultimately determined by that of the associated argument. Technically, the parameter variable itself is an AUTOMATIC variable containing the address of the corresponding argument. For convenience, a parameter may be considered to have a distinct "parameter" storage class. For example: DECLARE X FLOAT; CALL P (X); . . . P: PROCEDURE (Y); DECLARE Y FLOAT; In this example, X is an argument and Y is a parameter variable. A parameter variable is declared within a procedure declaration and receives a value when that procedure is invoked. Simple Open PL/I variable arguments are passed by reference. A parameter has a data type, perhaps a data type with extents, and in the simplest case, an argument passed to the parameter will have the same data type and extents as the parameter. If this is not the case, Open PL/I will, whenever possible, convert the argument to a value with the same data type and extents as the parameter and pass that value to the parameter. In the absence of specifications to the contrary, when a variable is passed as an argument to a parameter with matching data type and extents, the argument is passed "by reference." This means that it is the address of the actual variable itself that is passed as the argument, and the parameter is sharing storage with this variable. Hence, any assignment to the parameter is actually changing the argument variable itself. In other cases that is, when the argument is a variable whose data type and extents do not match the parameter, or when the argument is an expression, a constant, a function reference, or a simple variable enclosed in parentheses the argument is passed "by value." This means that the value of the 'argument is converted as described above and stored in a temporary location. It is the address of this temporary location that is passed as the argument. Hence, any assignment to the parameter can have no effect on any variable in the calling procedure. The precision of arithmetic data is part of its data type. This means that a Float Decimal(7) variable cannot be passed by reference to anything except a Float Decimal(7) parameter. The VARYING and ALIGNED attributes, as well as the declared string length, are part of a string variable's data type and must match those of the corresponding parameter for the parameter to share storage with the actual argument (true pass-by-reference). A picture variable can be passed by reference only if the corresponding parameter has an identical picture. The ALIGNED and UNALIGNED attributes of nonstring arguments and parameters must match. The VALUE attribute is an argument-passing attribute that indicates that the value and not the address of the parameter has been passed to the procedure. An array parameter with constant extents can be passed only those array arguments that have identical constant extents and an identical data type. A parameter structure can be passed only a structure or substructure of identical size, shape, and component data types. Array or structure arguments cannot be passed by value. The ANY attribute is an argument-passing attribute that specifies, for a parameter, that the corresponding argument can be of any data type. A procedure that has a character or bit-string parameter with a constant length may be called with string arguments of variable length or differing constant length. Since all such arguments are passed by value, they are effectively converted to the length of the parameter. For example:

CALL P(A,(B)); In this example, A is passed by reference if it matches its corresponding parameter and is passed by value if it does not. B is always passed by value. If the argument is an expression, function reference, built-in function reference, constant, parenthetical variable reference, or a reference to a variable whose data type does not match that of the parameter, the argument is copied to a temporary block of storage in the caller's stack frame and is said to be passed by value. The value of the variable in the caller will not be modified if it is passed by value. Actually, the address of the temporary is passed, and the parameter shares the storage allocated to the temporary. The extents of a parameter can be either integer constants or asterisks (*). If the parameter's extents are integer constants, any argument to be passed by reference must have identical constant extents. If the parameter's extents are asterisks, its corresponding argument may have extents of any value. In the latter case, the extents of the parameter are those of its corresponding argument. For example: DECLARE A(10) FLOAT; DECLARE B(25) FLOAT; . . . CALL P(A); . . . CALL P(B); . . . P: PROCEDURE(X); DECLARE X(*) FLOAT;

Storage Sharing
Open PL/I provides two storage classes that are explicitly designed to permit storage locations to be shared by several variables. These storage classes are BASED and DEFINED. (Use of the UNION attribute also allows variables to explicitly share storage.) Based variables are allocated no storage when they are declared. Instead, they serve to determine storage to be allocated by an ALLOCATE statement or to describe storage qualified by a pointer reference. Defined variables use the storage of a specified basis variable. A defined variable is declared with the DEFINED attribute, which also specifies the basis variable. A defined variable and its basis reference must satisfy one of the following criteria in order to share storage: 1. All variables sharing storage have identical data types.

2. All variables sharing storage are nonvarying character strings and therefore are suitable for character-string overlay defining. 3. All variables sharing storage are unaligned bit strings and therefore are suitable for bit-string overlay defining. A variable is suitable for overlay defining if it consists entirely of characters or bits that are packed into adjacent storage without gaps. (Gaps can be caused by the alignment of data in user-defined structures.)Variables suited for overlay defining are arrays, structures containing only the required type of string data, or scalar string variables. For example: DECLARE X(4,4) CHARACTER(1); DECLARE Y(16) CHARACTER(1) DEFINED(X); DECLARE Z CHARACTER(5) DEFINED(X); DECLARE 1 S BASED, 2 A CHARACTER(8), 2 B CHARACTER(8); In this example, X has a block of storage containing 16 characters. The entire block is shared by Y, but Z shares only the first 5 characters of the block. S could be used to access all 16 characters, while S.A could be used to access the first 8, and S.B could be used to access the last 8. A based character string of more than 16 characters would produce undefined results, but a string of 16 or fewer characters could be used. A similar example could be constructed using all unaligned bit data. Storage sharing of other data types requires that the data types match exactly, but an element of an array can be shared with a nonarray variable of the same data type. Similarly, a member of a structure can be shared with a nonmember of the same data type. Members of structures that have the attribute UNION also share physical storage locations with one another. A UNION is a variation of a structure in which all immediate members occupy the same physical storage location. Unions provide capabilities similar to those of defined variables. The UNION attribute, which can be used only in conjunction with a level number in a structure declaration, signifies that all immediate members of the major or minor designated structure occupy the same storage. An immediate member is any member having a level number greater than its containing structure but not greater than the level number of its fellow members. Typically, an immediate member has a level number that is one greater than its containing structure. (For more information on this attribute, see the section Union.) Arrays can share storage with other arrays only if they have the same data type and/or are equivalent leftto-right. Structures can share storage with other structures only if they are equivalent, left-to-right, to the structure whose storage is being shared. Left-to-right equivalence means that the sharing structures must be valid descriptions of the left part of the storage being shared. To be a valid description of the left part of storage, the sharing structures must have identical members up to and including all members contained anywhere within the last level-two item being shared. If any part of a level-two item is to be shared, all of it must be shared. For example: DECLARE 1 S, 2 A, 2 B, 3 C, 3 D; 2 E; In this example, a reference to T.B.C is a valid reference to S.B.C, but a reference to U.B.C is not valid, because the declaration of U does not describe the entire level-two item S.B. DECLARE 1 T BASED, 2 A, 2 B, 3 C, 3 D; DECLARE 1 U BASED, 2 A, 2 B, 3 C;

A picture variable can share storage only with other picture variables that have identical pictures. As is the case for argument/parameter matching, the ALIGNED, UNALIGNED, and VARYING attributes and the declared length of a string variable are part of its data type and must match, unless the strings qualify for string overlay sharing. The base, scale, and precision of arithmetic variables must always match if they are to share storage.

You might also like