Previous | Contents | Index |
A based variable is a variable that describes a data type associated with storage that will be accessed through a pointer or offset value. PL/I does not allocate any storage for a based variable. Instead, you must explicitly allocate storage.
When you declare a based variable, you provide PL/I with a description of the data that will be accessed by the variable. The actual data must be referenced by a pointer that contains the address of the data storage location. See Section 2.2.5 for information about the BASED attribute. The following example shows a declaration of a based variable:
DECLARE BUFFER CHARACTER(80) BASED (BUF_PTR), LINE CHARACTER(80), BUF_PTR POINTER; BUF_PTR = ADDR(LINE); |
The declaration of the variable BUFFER does not allocate any storage for it. Rather, PL/I associates the declaration of the variable with the pointer variable BUF_PTR. During the execution of the program, the value of the pointer variable is set to the location (address) in storage of the variable LINE by means of the ADDR built-in function. This effectively associates the description of BUFFER with the actual data value of LINE.
You can associate a based variable with a storage location by using the ADDR built-in function, as in the preceding example; by using the ALLOCATE statement; by using a locator-qualified reference to the based variable; by using the SET option of the READ statement; or by explicit allocation within an area.
The following sections cover these topics:
The data types most commonly associated with based variables are pointers, areas, and offsets.
A pointer is a variable whose value represents the location in memory of another variable or data item. Pointers are used to access based variables and buffers allocated by the system as a result of the SET option of the READ and ALLOCATE statements.
Areas are regions of storage in which based variables can be allocated and freed. The use of areas can simplify and speed operations involving large or numerous based variables.
An offset is a value indicating the location of a based variable
relative to the beginning of an area.
5.5.2 Allocation in Areas
PL/I supports storage management in areas (see Section 3.10 for a description of area data). If you use the ALLOCATE statement with an area (either implied or explicitly specified), you can cause the allocation of storage to be performed in that area, instead of in the general memory pool for based and controlled storage.
Storage management in areas has a number of uses, including the following:
You allocate storage in areas with the IN and SET options on the ALLOCATE statement, the AREA and OFFSET data attributes, and the EMPTY built-in function.
The IN option on the ALLOCATE statement takes a reference to an area. If the IN option is not specified, the area is implied from the SET option if the SET option specifies an offset variable with a base area. The SET option itself can be implied from the base variable. Whether an area is specified explicitly or implied, the allocation is performed in that area instead of in the available memory pool for based and controlled storage. If an error is detected in the process, the AREA condition is raised.
PL/I provides full support for allocation in areas as specified in the ANSI full PL/I language standard. In addition, it provides extensions to full PL/I, which enhance the usefulness of areas or provide for improved compatibility with other implementations of PL/I. These extensions are as follows:
A = EMPTY(); |
DECLARE A AREA(100) STATIC INITIAL(EMPTY()); |
Note that the last three items in this list are features that differ in some other implementations of PL/I.
For examples showing allocation in areas, see Section 5.5.7.
5.5.3 Referring to Based Variables
A reference to a based variable (except in an ALLOCATE statement) must specify a pointer or offset reference designating the storage to be accessed. This qualifying pointer or offset reference can be implicit, if it is specified with the BASED attribute, or explicit, if the based variable reference is prefixed with a locator qualifier. A complete based variable reference (with the locator qualifier) has the following form:
qualifying-reference -> based-reference |
Whether explicit or implicit, the qualifying reference must be to a pointer variable, a pointer-valued function, or an offset variable declared with a base area. The qualifying reference is evaluated each time the complete reference is evaluated and must yield a valid pointer value. If the qualifying reference is to an offset variable, the offset value is converted to a pointer using the base area specified in the offset variable's declaration.
You can use both implicit and explicit qualifications with the same based variable; the explicit qualifier overrides the implicit one. For example:
DECLARE X FIXED BIN BASED(P), P POINTER, (A,B) FIXED BIN; P = ADDR(A); X = ADDR(B)->X; |
In the second assignment statement, the reference to X on the left-hand side of the assignment has the implicit qualifier P, which is the address of the variable A. The reference to X on the right-hand side is explicitly qualified with the address of another variable, B. This assigns the value of B to the variable A.
In PL/I, you can obtain a valid pointer value in any of the following ways:
A pointer value is valid only as long as the storage to which it applies remains allocated. Moreover, a pointer obtained by the application of ADDR to a parameter or an automatic variable is valid only as long as the parameter's procedure invocation exists, even though the storage pointed to may exist longer.
The NULL built-in function returns a null pointer value that can be
assigned
to pointer and offset variables, but that is not valid for qualifying a
based variable reference.
5.5.4 Based Variables and Dynamic Storage Allocation
These subsections discuss the dynamic allocation of storage by the ALLOCATE statement and the READ SET statement.
Each time it is executed, the ALLOCATE statement allocates storage for a based variable and, optionally, sets a pointer or offset variable to the location of the storage in memory. The storage allocated can also be assigned values if the variable is declared with the INITIAL attribute. For example:
DECLARE LIST (10) FIXED BINARY BASED, (1) (LIST_PTR_A, LIST_PTR_B) POINTER; ALLOCATE LIST SET (LIST_PTR_A); (2) ALLOCATE LIST SET (LIST_PTR_B); (3) LIST_PTR_A -> LIST(1) = 10; (4) LIST_PTR_B -> LIST(1) = 15; |
The numbered items in this example are shown in Figure 5-2.
As you can see in this example, the array LIST is declared with the BASED attribute; however, the declaration does not reserve storage for this variable. Instead, the ALLOCATE statements allocate storage for the variable and set the pointers LIST_PTR_A and LIST_PTR_B to the storage locations. LIST_PTR_A and LIST_PTR_B must both be declared with the POINTER attribute.
In references, the different allocations of LIST can then be distinguished (unless the pointers are assigned new values) by locator qualifiers that identify the specific allocation of LIST.
The phrase LIST_PTR_A-> is a locator qualifier; it specifies the pointer that locates an allocation of storage for the variable. In this example, the first element of the storage pointed to by LIST_PTR_A is assigned the value 10. The first element of the storage pointed to by LIST_PTR_B is assigned the value 15.
Figure 5-2 Using the ALLOCATE Statement
Any extent expressions in the based variable declaration are evaluated each time the variable is allocated or referenced. Therefore, based variables can be used for data aggregates whose size depends on input data. Here is an example of dynamically allocating a matrix that will be accessed by several external procedures:
DECLARE 1 MATRIX_CONTROL_BLOCK STATIC EXTERNAL, 2 MATRIX_POINTER POINTER, 2 (ROW_SIZE,COL_SIZE) FIXED BINARY; DECLARE 1 MATRIX(ROW_SIZE,COL_SIZE) BASED(MATRIX_POINTER); GET LIST(ROW_SIZE,COL_SIZE); ALLOCATE MATRIX; |
The SET Option of the READ Statement
When you use the READ statement with a based variable, you do not have to define storage areas within your program to buffer records for I/O operations. If you specify the SET option on the READ statement, the READ statement places an input record in a system buffer and sets a pointer variable to the location of this buffer. For example:
DECLARE REC_PTR POINTER, NEW_BALANCE FIXED DECIMAL (6,2), INFILE FILE RECORD INPUT SEQUENTIAL; DECLARE 1 RECORD_LAYOUT BASED (REC_PTR), 2 NAME CHARACTER (15), 2 AMOUNT PICTURE '999V99', 2 BALANCE FIXED DECIMAL (6,2); . . . READ FILE (INFILE) SET (REC_PTR) ; . . . REC_PTR->BALANCE = NEW_BALANCE; REWRITE FILE (INFILE); |
In this example, the structure defined to describe the records in a file is declared with the BASED attribute; the declaration does not reserve storage for this structure. When the READ statement is executed, the record is read into a system buffer and the pointer REC_PTR is set to its location.
When you use the SET option with the READ statement, a subsequent REWRITE statement need not specify the record to be rewritten. PL/I rewrites the record indicated by the pointer variable specified in the READ statement.
Figure 5-3 shows this example.
Figure 5-3 Using the READ Statement with a Based Variable
The ADDR built-in function returns the storage location of a variable. You can use it to associate the storage occupied by a variable with the description of a based variable. For example:
DECLARE A FIXED BINARY BASED (X), B FIXED BINARY, X POINTER; X = ADDR (B); A = 15; |
In this example, the variable A is declared as a based variable, with X designated as its pointer. The variable B is an automatic variable; PL/I allocates storage for B when the block is activated. When the ADDR built-in function is referenced, it returns the location in storage of the variable B, and the assignment statement gives this value to the pointer X. This assignment associates the variable A with the storage occupied by B. Because A is based on X and X points to B, an assignment statement that gives a value to A actually modifies the storage occupied by the variable B. Figure 5-4 shows this example.
Figure 5-4 Using the ADDR Built-In Function
In most applications, the data type of a based variable reference is
identical to the data type under which the accessed storage is
allocated. However, it is not required that the data types be
identical. The following sections discuss type-matching criteria in
more detail.
5.5.6.1 Matching by Overlay Defining
Matching by overlay defining is in effect if the based variable reference and the variable for which the storage was originally allocated are both suitable for character-string or bit-string overlay defining. The only further restriction is that the size n (in characters or bits) of the based variable reference must be less than or equal to the size in characters or bits of the original variable. The based variable reference accesses the first n characters or bits of the storage.
The first program in Section 5.5.7 contains an example of this type of
matching. The structure members PAY_RECORD.GROSS_PAY (a character
string) and HEALTH_RECORD.EXAM DATE (a picture) are not identical data
types. However, both are stored as a character string of length 9;
therefore, they meet the criteria for string overlay defining and for
data-type matching.
5.5.6.2 Matching by Left-to-Right Equivalence
Matching by left-to-right equivalence applies to structured variables that are identical up to a certain point. To see if this applies, examine the declaration of the based variable, and consider only the portion on the left that includes the referenced member and all of the level-2 substructure containing the referenced member (if the member is not itself at level 2). If the original variable's declaration has a similar left part with identical data type, then the based variable reference and the original reference match. For example:
DECLARE 1 S1 BASED (P), 2 X, 3 (A,B) FIXED BIN, 2 Y, 3 C CHAR(10), 3 D(5) FLOAT; DECLARE 1 S2 BASED(P), 2 X, 3 (A,B) FIXED BIN, 2 Y, 3 C CHAR(10), 3 E BIT(32); ALLOCATE S1; S2.A = 3; /* valid l-to-r match */ S2.C = 'X'; /* INVALID */ |
In the first assignment, S2.A is a valid reference because S1 and S2 match through the level-2 structure X. In the second assignment, S2.C is invalid in standard PL/I because the level-2 structures S2.Y and S1.Y do not match. However, the reference to S2.C does work.)
This sort of matching is useful in connection with data structures and files, where the first part of a record contains a value indicating the precise structure of the remainder of the record.
Note that the UNION attribute allows this type of declaration to be
written more easily.
5.5.6.3 Nonmatching Based Variable References
In PL/I, a based variable reference need not match the variable for which the storage was originally allocated. The only requirement is that the size of the based variable in bits be less than or equal to the size of the original variable in bits. However, use of such nonmatching references requires knowledge of the internal representation of data. You should not expect the resulting code to be transportable between OpenVMS systems or to other vendors' hardware. For example:
DECLARE X FLOAT BINARY(24); DECLARE 1 S BASED(ADDR(X)), 2 FRAC_1 BIT(7), 2 EXP BIT(8), 2 SIGN BIT(1), 2 FRAC_2 BIT(16); EXP = '0'B; /* set exponent to 0 */ SIGN = '1'B; /* set sign negative */ X = X + 1; |
The declaration of S describes the internal VAX representation of a
single-precision floating-point number. The first two assignments set
the sign and exponent fields to the reserved operand combination; the
assignment to X causes a reserved operand exception.
5.5.7 Examples of Based Variables
The program DEFINED uses based variables and the READ SET statement to process a file of personnel data (PERSONNEL.DAT). The file has two types of valid records, a pay record and a health record, which are identified by a 1-character code in the first position. The two record types are declared as based structures (PAY_RECORD and HEALTH_RECORD), one of which is selected based on the record type character ('P' for pay, 'E' for health). Any record that does not begin with one of these characters is invalid and is written out as a reference to the based character variable INVALID_RECORD.
DEFINED: PROCEDURE OPTIONS(MAIN); DECLARE P POINTER; /* pointer to structures */ DECLARE 1 PAY_RECORD BASED(P), 2 RECORD_TYPE CHARACTER(1), 2 NAME CHARACTER(20), /* the two structures differ in this member: */ 2 GROSS_PAY PICTURE '999999V.99'; DECLARE 1 HEALTH_RECORD BASED(P), 2 RECORD_TYPE CHARACTER(1), 2 NAME CHARACTER(20), 2 EXAM_DATE CHARACTER(9); DECLARE INVALID_RECORD CHARACTER(30) BASED(P); DECLARE PERSONNEL RECORD FILE; DECLARE PERSOUT STREAM OUTPUT PRINT FILE; /* used to control DO group: */ %REPLACE NOTENDFILE BY '1'B; ON ENDFILE(PERSONNEL) BEGIN; PUT FILE(PERSOUT) SKIP LIST ('All processing complete.'); STOP; /* program stops here */ END; OPEN FILE(PERSONNEL) INPUT TITLE('PERSONNEL.DAT'); DO WHILE(NOTENDFILE); /* terminated by ENDFILE ON-unit */ READ FILE(PERSONNEL) SET(P); /* P is the location of the record acquired by the READ statement */ IF P->PAY_RECORD.RECORD_TYPE = 'P' THEN PUT FILE(PERSOUT) SKIP LIST ('Name=',P->PAY_RECORD.NAME, 'Gross pay=',P->GROSS_PAY); ELSE /* either a health record or an invalid record */ DO; IF P->HEALTH_RECORD.RECORD_TYPE = 'E' THEN PUT FILE(PERSOUT) SKIP LIST ('Name=',P->HEALTH_RECORD.NAME, 'Exam date:',P->EXAM_DATE); ELSE /* invalid record type */ PUT FILE(PERSOUT) SKIP LIST ('Invalid record:',P->INVALID_RECORD); END; END; /* repeat DO group until ENDFILE is signaled */ END DEFINED; |
For example, assume that the file PERSONNEL.DAT contains these records:
PMary A. Ford 125000.55 EMary A. Ford 22July 80 t12345678901234567890pppppp.pp |
Name= Mary A. Ford Gross pay= 125000.55 Name= Mary A. Ford Exam date: 22July 80 Invalid record: t12345678901234567890pppppp.pp All processing complete. |
Notice these other features of the program:
The UNION attribute can be used to declare a single record with a variant portion in place of PAY_RECORD and HEALTH_RECORD. For example:
1 RECORD BASED(P), 2 RECORD_TYPE CHARACTER(1), 2 NAME CHARACTER(20), 2 VARIANS UNION, 3 GROSS_PAY PICTURE '999999V.99', 3 EXAM_DATE CHARACTER(9) |
Note that the UNION attribute is not available in many other PL/I implemenations.
Previous | Next | Contents | Index |