A controlled variable is a variable whose actual storage is allocated and freed dynamically in generations, of which only the most recent is accessible to the program. Controlled variables are declared with the CONTROLLED attribute. A controlled variable can be a scalar, array, area, or major structure variable possessing any of the attributes that do not conflict with the CONTROLLED attribute. See Section 2.2.11 for information about the CONTROLLED attribute.
The CONTROLLED attribute cannot be applied to minor structures, members of structures, parameters, or descriptions in an ENTRY or RETURNS attribute.
A controlled variable has no storage assigned to it until an ALLOCATE statement allocates storage for it. Each storage assignment is a generation of the variable. Subsequent ALLOCATE statements allocate subsequent generations. At any time in the program's execution, a reference to a controlled variable is a reference to the most recent generation of that variable, that is, the generation created by the most recent ALLOCATE statement.
The FREE statement frees the most recent generation of a controlled variable. If an attempt is made to free a controlled variable for which no generation exists (or to refer to such a variable), PL/I signals the ERROR condition. The following example shows the use of controlled variables:
CONT: PROCEDURE OPTIONS (MAIN); DECLARE STR CHARACTER (10) CONTROLLED; ALLOCATE STR; STR = 'First'; ALLOCATE STR; STR = 'Second'; ALLOCATE STR; STR = 'Third'; PUT SKIP LIST (STR); FREE STR; PUT SKIP LIST (STR); FREE STR; PUT SKIP LIST (STR); FREE STR; END;
The output of this program is as follows:
Third Second First
Because only the most recent generation of a controlled variable is available to a program, controlled variables provide an easy way to implement a stack. The ALLOCATE statement is equivalent to a push operation, and the FREE statement is equivalent to a pop operation. The ALLOCATION built-in function returns the number of generations of a variable, so you can use it to find out if the stack is empty. For example:
DECLARE NEXT_MOVE CHARACTER(5) CONTROLLED, DIRECTIONS(4) CHARACTER(5) INITIAL( 'North','East','South','West'), D FIXED BINARY (7); . . . ALLOCATE NEXT_MOVE; /* Part of a loop that reports */ NEXT_MOVE = DIRECTIONS(D); /* moves in reverse order */ . . . DO WHILE /* Print moves in correct order */ (ALLOCATION(NEXT_MOVE) ^= 0); PUT SKIP LIST ('Go ', NEXT_MOVE); FREE NEXT_MOVE; END;
See Section 11.4.7 for more information about the ALLOCATION built-in
5.6.2 Using the ADDR Built-In Function
You can use a controlled variable as the argument of the ADDR built-in function. If a generation exists, ADDR returns a pointer to it. If no generation of the variable exists, ADDR returns the null pointer. Thus, you can use ADDR to preserve a pointer to a generation of a controlled variable that later becomes hidden under further generations, as in the following example:
DECLARE STOPS CHARACTER (20) VARYING CONTROLLED, MIDPOINT CHARACTER (20) VARYING BASED (P), P POINTER; . . . ALLOCATE STOPS; STOPS = CURRENT_LOC; IF I = 5 THEN P = ADDR(STOPS); . . . PUT SKIP LIST ( 'End reached! Halfway point was', MIDPOINT);
At a certain point during the execution of this program, the ADDR built-in function captures the address of the current generation of STOPS and assigns it to P. After more generations of STOPS have been allocated, MIDPOINT (which is based on P) has the value of that same intermediate generation of STOPS.
Note that the value of P (and therefore of MIDPOINT) is valid only so long as the intermediate generation of STOPS to which P points is allocated. As soon as that generation is freed, the value of P becomes invalid, and it must not be used in a pointer-qualified reference until it is reassigned.
A controlled variable cannot be used in a pointer-qualified reference. In the previous example, a reference like the following would be illegal:
This section describes the mechanisms for dynamically allocating
5.7.1 ALLOCATE Statement
The ALLOCATE statement obtains storage for a based or controlled variable and sets (with based variables) a locator variable equal to the address of the allocated storage. The format of the ALLOCATE statement is as follows:
allocate-itemThe syntax of the allocate item is:
variable-reference [SET(locator-reference)] [IN(area-reference)]
variable-referenceA based or controlled variable for which storage is to be allocated. The variable can be any scalar value, array, area, or major structure variable; it must be declared with the BASED or CONTROLLED attribute.
SET(locator-reference)The specification of a pointer or offset variable (for based variables) that is assigned the value of the location of the allocated storage. If the SET option is omitted, the based variable must be declared with BASED(locator-reference); the variable designated by that locator reference is assigned the location of the allocated storage.
You cannot use the SET option to allocate controlled variables.
IN(area-reference)The specification of an area reference (for based variables) in which the storage is to be allocated. If the IN option is omitted, the SET option (or implied SET option if the locator variable is an offset) must be an offset declared with OFFSET(area-reference).
You cannot use the IN option to allocate controlled variables.
DECLARE STATE CHARACTER(100) BASED (STATE_POINTER), STATE_POINTER POINTER; ALLOCATE STATE;
This ALLOCATE statement allocates storage for the variable STATE and sets the pointer STATE_POINTER to the location of the allocated storage.
The ALLOCATE statement obtains the amount of storage needed to accommodate the current extent of the specified variable. If, for example, a character-string variable is declared with an expression for its length, the ALLOCATE statement evaluates the current value of the expression to determine the amount of storage to be allocated. For example:
DECLARE BUFFER CHARACTER (BUFLEN) BASED, BUF_PTR POINTER; . . . BUFLEN = 80; ALLOCATE BUFFER SET (BUF_PTR);
Here, the value of BUFLEN is evaluated when the ALLOCATE statement is executed. The ALLOCATE statement allocates 80 bytes of storage for the variable BUFFER and sets the pointer variable BUF_PTR to its location.
The ALLOCATE statement is also used to allocate storage for controlled variables. A controlled variable is one whose actual storage is allocated and freed dynamically in generations, only the most recent of which is accessible to the program. Unlike based variables, a controlled variable cannot be used in a pointer-qualified reference.
If the variable being allocated has been declared with initial values,
these values are assigned to the variable after allocation.
5.7.2 FREE Statement
The FREE statement releases the storage that was allocated for a based or controlled variable. The format of the FREE statement is as follows:
FREE free-item[,free-item ...];
free-itemThe syntax of the free-item is:
variable-referenceA reference to the based or controlled variable whose storage is to be released.
If you do not explicitly free the storage acquired by the variable, the storage is not freed until the program terminates.
If you free a variable that is explicitly associated with a pointer, the pointer variable becomes invalid and must not be used to reference storage. You can only free a variable once for each allocation.
IN(area-reference)The specification of an area reference (for based variables) in which the storage is to be freed. If the IN option is omitted, the variable reference must be either implicitly or explicitly based on an offset variable with a base area.
You cannot use the IN option in conjunction with controlled variables.
FREE LIST; FREE P->INREC;
These statements release the storage acquired for the based variable LIST and for the allocation of INREC pointed to by the pointer P.
ALLOCATE STATE SET (STATE_PTR); . . . FREE STATE;
This FREE statement releases the storage for the based variable STATE
and makes the value of STATE_PTR undefined.
5.7.3 Other Mechanisms for Dynamic Storage Allocation
PL/I has a variety of dynamic storage management mechanisms available besides those for based and controlled variables. You can also use the following mechanisms:
These storage control mechanisms are generally similar in the amount of overhead that they require both in execution time and in storage space, although certain mechanisms have characteristics that make them useful in specific circumstances.
In general, the standard PL/I language manipulation of dynamic memory
provides reasonable performance with some built-in checking.
5.8 Defined Variables
The DEFINED attribute indicates that PL/I is not to allocate storage for the variable, but is to map the description of the variable onto the storage of another variable called the base variable. The DEFINED attribute provides a way to access the same data using different names (see Section 2.2.13 for a description of the DEFINED attribute).
In a declaration of a defined variable, the DEFINED keyword, which you can abbreviate to DEF, is followed by a variable reference (which must not have the BASED or DEFINED attribute), and optionally by the position in the variable at which the defined variable begins. If you specify the position, you use the POSITION attribute followed by an expression in parentheses. The expression is an integer expression that specifies a position in the base; a value of 1 indicates the first character or bit. You can use the POSITION attribute only when the defined variable satisfies the rules for string overlay defining, which is described later in this section.
When you use the DEFINED attribute in the declaration of a variable, PL/I associates the description of the variable in the declaration with the storage allocated for the variable on which the declaration is defined. For example:
DECLARE NAMES(10) CHARACTER(5) DEFINED (LIST), LIST(10) CHARACTER(5);
In this example, the variable NAMES is a defined variable; its data description is mapped to the storage occupied by the variable LIST. Any reference to NAMES or to LIST is resolved to the same location in memory.
With defined variables that meet the criteria for string overlay defining, you can use the POSITION attribute to specify the position in the base variable at which the definition begins. For example:
DECLARE ZIP CHARACTER(20), ZONE CHARACTER(10) DEFINED(ZIP) POSITION(4);
This statement declares the variable ZONE and maps it to characters 4 through 13 of the variable ZIP.
The extent of a defined variable is determined at the time of block activation, but the base reference (and the position, if the POSITION attribute is also specified) is interpreted each time the defined variable is referenced. For example:
DECLARE I FIXED, A(10) FIXED, B FIXED DEFINED(A(I)); DO I = 1 TO 10; B = I; END;
The DO group assigns I to A(I) for I = 1,2,...10.
The base reference of a defined variable cannot be a reference to a based variable or to another defined variable. A defined variable and its base reference must satisfy one of the following criteria:
If the defined variable is specified with the POSITION attribute, then both the defined variable and the base reference must be suitable for bit- or character-string overlay defining.
In brief, a variable is suitable for overlay defining if it consists entirely of characters or bits, and those characters or bits are packed into adjacent storage without gaps. Such a variable can be treated as a string or interpreted as different types of aggregates. For example:
DECLARE A (10) CHARACTER (5); DECLARE B (5) CHARACTER (10) DEFINED (A); A (1) = 'AAAAA'; A (2) = 'BBBBB'; PUT LIST (B(1));
Figure 5-5 shows a 50-byte region of storage treated either as a 10-element array (A) of 5-character strings or as a 5-element array (B) of 10-character strings.
Figure 5-5 An Overlay Defined Variable
If the defined variable and its base reference have identical data types, a reference to the defined variable is equivalent to the base reference. In the case of overlay defining, the defined variable maps onto part of the base reference's storage as follows:
A variable V is suitable for character-string overlay defining if V is not an unconnected array and if one of the following criteria is satisfied:
A variable V is suitable for bit-string overlay defining if V is not an unconnected array and if one of the following criteria is satisfied:
Variables that have any of the attributes BASED, DEFINED, UNION, or PARAMETER can share physical storage locations with one or more other variables.
A based variable is not allocated any storage when it is declared. Instead, storage is either located by a locator-qualified reference to the variable or allocated by the ALLOCATE statement. The BASED attribute then allows you to describe the characteristics of a variable, which can then be located by a reference that qualifies the variable's name with any valid pointer value. Based variables are useful when the program must control the allocation of storage for several variables with identical attributes. The creation and processing of a queued or linked list is a common case. For full details on based variables and valid pointer values, see Section 5.5.
A defined variable uses the storage of a previously declared variable, which is referenced in the DEFINED attribute. The referenced variable is known as the base of the defined variable. The base can be a character- or bit-string variable, suitable for a technique called string overlay defining. When the base is a string variable, the POSITION attribute can also be specified for the defined variable, giving the position within the base variable's storage at which the overlay defining begins. Defined variables are useful when the program must refer to the same storage by different names. For full details, see Section 5.8
Unions provide capabilities similar to those of defined variables, but the rules governing unions are less restrictive. A union is a variation of a structure in which all immediate members occupy the same storage.
The UNION attribute, which is 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. Immediate members are those members having a level number one higher than the major or minor structure with the union attribute. For more details, see Section 4.2.2
Parameters of a procedure share storage with their associated arguments. The associated argument is either a variable written in the argument list or a dummy variable allocated by the compiler. When the written argument is a variable, the sharing of storage by the parameter and argument allows a procedure to return values to the invoking procedure by changing the value of the parameter. For instance, a function can return values in this manner in addition to returning the value specified in its RETURN statement. For more information, see Section 7.5.