Chapter 4
Aggregates
An aggregate is a data structure, either an array or structure composed of items as follows:
An array is an aggregate in which all items, called elements, have the same data type. An individual element of an array is referred to by an integer subscript that designates the element's position, or order, in the array. Elements can be scalar data items or aggregates.
A structure is an aggregate in which individual items, called members, can have different data types. Individual members are referred to by qualified references. Members can be scalar data items or aggregates.
4.1. Arrays
Arrays provide an orderly way to manipulate related variables of the same data type. An array variable is defined in terms of the number of elements that the array contains and the organization of those elements. These attributes of an array are called its dimensions.
4.1.1. Array Declarations
To declare an array, specify its dimensions in a DECLARE statement using one of the following syntaxes:
DECLAREidentifier[DIMENSION] (bound-pair,...) [attribute...];
DECLARE (identifier[DIMENSION] (bound-pair,...)) [attribute...];
To declare two or more array variables that have the same dimensions, bounds, and attributes, use the following syntax:
DECLARE (identifier, ...) [DIMENSION] (bound-pair,...) [attribute...];
identifierA valid PL/I identifier to be used as the name of the array.
bound-pairA specification of the number of elements in each dimension of the array. A bound pair can consist of one of the following:
Two expressions separated by a colon giving the lower and upper bounds for that dimension
A single expression giving the upper bound only (the lower bound is then 1 by default)
An asterisk (*), used in the declaration of array parameters, indicating that the parameter can be matched to array arguments with varying numbers of elements in that dimension
Bound pairs in series must be separated by commas, and the list of bound pairs must be enclosed in parentheses. The list of bound pairs must immediately follow the identifier or the optional keyword DIMENSION or the list of declarations. The following rules apply to specifying the dimensions of an array and the bounds of a dimension:
An array can have up to eight dimensions.
The values you can specify for bounds are restricted as follows:
If the array has the STATIC attribute, you must specify all bounds as restricted integer expressions. A restricted integer expression is one that yields only integral results and has only integral operands, which can be evaluated at translation time. Such an expression can use only the addition (+), subtraction (-), and multiplication (*) operators.
If the array has the AUTOMATIC, BASED, CONTROLLED, or DEFINED attribute, you can specify the bounds as optionally signed integer constants or as expressions that yield integer values at run time. If the array has AUTOMATIC or DEFINED, the expressions must not contain any variables or functions that are declared in the same block, except for parameters.
The value of the lower bound you specify must be less than the value of the upper bound.
Table 4.1, “Specifying Array Dimensions” shows several forms of bound pairs as used in declarations. Note that all the examples in Table 4.1, “Specifying Array Dimensions” would be identical in effect if the optional keyword DIMENSION were added.
attributeOne or more data type attributes of the elements of the array. All attributes you specify apply to each of the elements in the array.
Elements of an array can have any data type. If the array has the FILE or ENTRY attribute, it must also have the VARIABLE attribute.
| Bound Pairs | Examples |
|---|---|
|
ARRAY_NAME (bound) A single value specifies:
|
DECLARE VERBS (6) CHARACTER (12) ; |
| ARRAY_NAME (low-bound:high-bound) | |
|
A single range of values specifies:
|
DECLARE TEMPERATURES (-60:120) ; |
|
ARRAY_NAME (bound1,bound2,...) A list of values specifies:
|
DECLARE TABLE (10,10) FIXED BINARY ; DECLARE SETS (5,5,5,5) CHARACTER (80) ; |
| ARRAY_NAME (low-bound1:high-bound1,low-bound2,high-bound2,...) | |
|
A list of ranges specifies:
|
DECLARE WINDOWS (1:10,-2:32) FIXED; DECLARE HISTORIES (10,30:102,50) ... |
|
ARRAY_NAME (*,...) Asterisk extents specify:
|
ADDIT: PROCEDURE (ARR) ; DECLARE ARR(*,*) FIXED ; |
The declaration of an array specifies its dimensions, the bounds of each dimension, and the attributes of the elements.
One bound pair is specified for each dimension of the array to define the number of elements in that dimension. The total number of elements in the array, called its extent, is the product of the number of elements in all the dimensions of the array. If omitted, the lower bound is 1 by default.
You can use an asterisk (*) as the bound pair when you declare arrays as parameters of a procedure; the asterisk indicates that the parameter can accept array arguments with any number of elements. (If one dimension is specified with an asterisk, all must be specified with asterisks.) For example:
DECLARE SALARIES (100) FIXED DECIMAL (7,2);
This statement declares a 100-element array with the identifier SALARIES. Each element is a fixed-point decimal number with a total of seven digits, two of which are fractional.
The following statement declares a two-dimensional array of 64 integers:
DECLARE GAME_BOARD (8,8) FIXED BINARY (7);
The following statement declares a one-dimensional array of 12 character strings, each having a length of 2:
DECLARE PM_HOURS(13:24) CHARACTER(2);
The elements of the previous array is numbered 13 through 24 instead of 1 through 12.
You can replace the identifier in a statement with a list of declarations, which declares several arrays with the same attributes. For example:
DECLARE (SALARIES,PAYMENTS)(100) FIXED DECIMAL(7,2);
This statement declares SALARIES and another array, PAYMENTS, with the same dimensions and other attributes.
4.1.2. References to Individual Elements
You refer to an individual element in the array with subscripts. Because an array's attributes are common to all of its elements, a subscripted reference has the same properties as a reference to a scalar variable with those attributes.
You must enclose subscripts in parentheses in a reference to an array element. For example, in a one-dimensional array named ARRAY declared with the bounds (1:10), the elements are numbered 1 through 10 and are referred to as ARRAY(1), ARRAY(2), ARRAY(3), and so on. The lower and upper bounds that you declare for a dimension determine the range of subscripts you can specify for that dimension.
The lower and upper bounds that you declare for a dimension determine the range of subscripts that you can specify for that dimension. The number of elements in any dimension of any array is:
(upper bound) - (lower bound) + 1
For multidimensional arrays, the subscript values represent an element's position with respect to each dimension in the array. Figure 4.1, “Specifying Elements of an Array” shows subscripts for elements of one-, two-, and three-dimensional arrays. In subscripted references for multidimensional arrays, the number of subscripts must match the number of dimensions of the array and must be separated by commas.
You can specify the subscript of an array element using any variables or expressions having integer values, that is, values that can be expressed as fixed binary or fixed decimal with a zero scale factor. For example:
DECLARE DAYS_IN_MONTH(12) FIXED BINARY; DECLARE (COUNT, TOTAL) FIXED BINARY; TOTAL = 0; DO COUNT = 1 TO 12; TOTAL = TOTAL + DAYS_IN_MONTH(COUNT); END;
Here, the variable COUNT is used as a control variable in a DO loop. As the value of COUNT is incremented from 1 to 12, the value of the corresponding element of the array DAYS_IN_MONTH is added to the value of the variable TOTAL.
4.1.3. Initializing Arrays
Specify the INITIAL attribute for an array to initialize its values in the declaration. For example:
DECLARE MONTHS (12) CHARACTER (9) VARYING INITIAL ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
Each element of the array MONTHS is assigned a value according to the order of the character-string constants in the initial list: MONTH(1) is assigned the value 'January'; MONTH(2) is assigned the value 'February'; and so on.
If the array being initialized is multidimensional, the initial values are assigned in row-major order.
To assign identical initial values to some or all elements of an array, you can use an iteration factor with the INITIAL attribute. For example:
DECLARE TEST_AVGS (30,4) FIXED DECIMAL (5,2) STATIC INITIAL ((120) 50);
This statement declares the array TEST_AVGS with 120 elements, each of which is given an initial value of 50.
You can also use the asterisk (*) iteration factor to initialize all the elements of an array to the same value. For example:
DECLARE TEST_AVGS (30,4) FIXED DECIMAL (5,2) STATIC INITIAL ((*) 50);
This statement also declares the array TEST_AVGS with 120 elements, each of which is given an initial value of 50.
Although Endless PL/I V3.9 for OpenVMS VAX and Endless PL/I V5.0 for OpenVMS Alpha both support the initialization of automatic arrays with the INITIAL attribute, for the following reasons this is not always the most efficient way (in terms of program compilation and execution) to initialize array elements:
When you initialize elements in an array that has the AUTOMATIC, BASED, or CONTROLLED attribute, the compiler does not check that all elements are initialized until run time. Thus, you do not receive any compile-time checking of initialization, even if you used constants to specify the array bounds and iteration factors.
Your programs will run more efficiently if you initialize automatic arrays with assignment statements rather than the INITIAL attribute.
If the array is not modified by your program, you can increase program efficiency by declaring the array with the STATIC and READONLY attributes and using the INITIAL attribute to initialize its elements. In this case, the compiler checks that you have initialized all the elements and that they are valid.
Iteration Factors
When more than one successive element of an array is to be assigned the same value with the INITIAL attribute, you can specify an iteration factor. An iteration factor indicates the number of times that a specified value is to be used in the assignment of values to elements of an array. You can specify an iteration factor in one of the following formats:
(
iteration-factor) arithmetic-constant(
iteration-factor) scalar-reference(
iteration-factor) (scalar-expression)(
iteration-factor) *
iteration-factorAn unsigned decimal constant indicating the number of times the specified value is to be used in the assignment of an array element. The iteration factor can be zero.
arithmetic-constantAny arithmetic constant whose data type is valid for conversion to the data type of the array.
scalar-referenceA reference to any scalar variable or to the NULL built-in function.
scalar-expressionAny arithmetic or string expression or string constant. The expression or constant must be enclosed in parentheses.
- *
Symbol used to indicate that the corresponding array element is not to be assigned an initial value.
You can use any of these forms for arrays that have the AUTOMATIC attribute. For arrays with the STATIC attribute, you can use only constants and the NULL built-in function.
For example, the following declaration of the array SCORES initializes all elements of the array to 1:
DECLARE SCORES (100) FIXED STATIC INITIAL ((100)1);
The next declaration initializes the first 50 elements to 1 and the last 50 elements to -1:
DECLARE SCORES (100) FIXED STATIC INITIAL ((50)1,(50)-1);
The following declaration initializes the first 49 elements to 1; the next 2 elements are not initialized; and the next 49 elements are initialized to -1:
DECLARE SCORES (100) FIXED STATIC INITIAL ((49)1,(2)*,(49)-1);
The declaration in the next example initializes all 10 elements of an array of character strings to the 26-character value in apostrophes. The string constant is enclosed in parentheses; this is required to differentiate between iteration factors and replication factors.
DECLARE ALPHABETS (10) CHARACTER(26) STATIC INITIAL((10)('ABCDEFGHIJKLMNOPQRSTUVWXYZ'));
4.1.4. Assigning Values to Array Variables
You can specify an array variable as the target of an assignment statement in the following cases:
array-variable=expression;
This is valid where the expression yields a scalar value. Every element of the array is assigned the resulting value. The array variable must be a connected array whose elements are scalar. You can use the asterisk in an assignment:
array-variable(*,...) =expression;
You can use a single asterisk regardless of how many dimensions an array has, or you can use an asterisk for each dimension.
Note that the arithmetic operators, such as the plus sign (+) and the minus sign (-), cannot have arrays as operands. An assignment of the following form is invalid:
ARRAYC = ARRAYA + ARRAYB;
The following assignment is valid where the specified array variables have identical data-type attributes and dimensions:
array-variable-1=array-variable-2;
Each element in array-variable-1 is assigned the value of the corresponding element in
array-variable-2. In this type of assignment, both arrays must be connected. The actual storage
they occupy must not overlap, unless the arrays are identical.
All other specifications of an array variable as the target of an assignment statement are invalid.
4.1.5. Order of Assignment and Output for Multidimensional Arrays
When a multidimensional array is initialized, or when it is assigned values without references to specific elements, PL/I assigns the values in row-major order. In row-major order, the rightmost subscript varies the most rapidly. For example, an array can be declared as follows:
DECLARE TESTS (2,2,3);
If TESTS is specified in a GET statement or in a declaration with the INITIAL attribute, values are assigned to the elements in the following order:
TESTS (1,1,1)
TESTS (1,1,2)
TESTS (1,1,3)
TESTS (1,2,1)
TESTS (1,2,2)
TESTS (1,2,3)
TESTS (2,1,1)
TESTS (2,1,2)
TESTS (2,1,3)
TESTS (2,2,1)
TESTS (2,2,2)
TESTS (2,2,3)
When an array is output with a PUT statement, PL/I uses the same order to output the array elements. For example:
PUT LIST (TESTS);
This PUT statement outputs the contents of TESTS in the order previously shown.
4.1.5.1. Using GET and PUT Statements with Array Variables
When
you specify an array variable name in the input-target list of a GET LIST or GET EDIT
statement, elements of the array are assigned values from the data items in the input stream. For example:
DECLARE VERBS (6) CHARACTER (15) VARYING; GET LIST (VERBS);
When this GET LIST statement executes, it accepts data from the default input stream. Each input field delimited by
blanks, tabs, or commas is considered a separate string. The values of these strings are assigned to elements of the array
VERBS in the order VERBS(1), VERBS(2),...VERBS(6). If a multidimensional array appears in an
input-target list, input data items are assigned to the array elements in row-major
order.
An array can also appear, with similar effects, in the output-source list of a PUT
statement.
4.1.6. Passing Arrays as Arguments
You can pass an array variable as an argument to another procedure. Within the invoked procedure, the corresponding parameter must be declared with the same number of dimensions. The rules for specifying the bounds in a parameter descriptor for an array parameter are as follows:
If you specify the bounds with integer constants, they must match exactly the bounds of the corresponding argument.
You can specify all bounds as asterisks (*). Then, the bounds of the array are determined from the bounds of the corresponding argument when the procedure is actually invoked. If any bound is specified as an asterisk, all bounds must be specified as asterisks.
For example:
DECLARE SCAN ENTRY ((5,5,5) FIXED,(*) FIXED), MATRIX (5,5,5) FIXED, OUTPUT (20) FIXED; CALL SCAN (MATRIX,OUTPUT);
The procedure SCAN receives two arrays as arguments. The first is a three-dimensional array whose bounds are known. The second is a one-dimensional array whose bounds are not known. The procedure SCAN can declare these parameters as follows:
SCAN: PROCEDURE (IN,OUT); DECLARE IN (*,*,*) FIXED, OUT (*) FIXED;
An array whose storage is unconnected cannot be passed as an argument. Arrays are always passed by reference.
4.1.7. Built-In Functions Providing Array Dimension Information
PL/I provides the following built-in functions that return information about the dimensions of an array:
For the first dimension of an array X, the relationship of these functions can be expressed as follows:
DIMENSION (X,1) = HBOUND (X,1) - LBOUND (X,1) + 1
The procedure that follows uses the HBOUND and LBOUND built-in functions:
ADDIT: PROCEDURE (X); DECLARE X (*) FIXED BINARY, (COUNT,I) FIXED BINARY; COUNT = 0; DO I = LBOUND (X,1) TO HBOUND(X,1); COUNT = COUNT + 1; X(I) = COUNT; END; RETURN; END;
This procedure receives a one-dimensional array as a parameter and initializes the elements of the array with integral values beginning with 1.
4.2. Structures
A structure is a data aggregate consisting of one or more members. The members can be scalar data items or aggregates. Different members can have different data types. Structures are useful when you want to group related data items having different data types.
4.2.1. Structure Declarations and Attributes
The declaration of a structure defines its organization and the names of members at each level in the structure. The major structure name is declared as structure level 1; minor members must be declared with level numbers greater than 1. For example:
DECLARE 1 PAYROLL, 2 NAME, 3 LAST CHARACTER(80) VARYING, 3 FIRST CHARACTER(80) VARYING, 2 SALARY FIXED DECIMAL(7,2);
This statement declares a structure named PAYROLL. You can access the last name with a qualified reference:
PAYROLL.NAME.LAST = 'ROOSEVELT';Alternatively, because the last and first names have the same attributes, you can declare the same structure as follows:
DECLARE 1 PAYROLL, 2 NAME, 3 (LAST,FIRST) CHARACTER(80) VARYING, 2 SALARY FIXED DECIMAL(7,2);
The following additional rules apply to the specification of level numbers:
Level numbers must be specified with decimal integer constants.
A level number must be separated from its associated variable name by at least one space or tab character.
Level numbers after level 1 can have any integer value, as long as each level number is equal to or greater than the level number of the preceding level. (There can be only one level 1.)
Each identifier in the structure must be separated from the declaration of the previous identifier by a comma.
Substructures at the same logical level of nesting do not have to have the same level number.
The deepest possible logical level is 15.
The largest possible level number constant is 32767.
Attributes for Structure Variables
Within a structure, you can only declare members at the lowest level of each substructure with data-type attributes. Additional rules for specifying attributes for the various components of a structure are as follows:
Only the following attributes are valid for the major structure:
AUTOMATIC GLOBALREF BASED INTERNAL CONTROLLED READONLY DEFINED STATIC EXTERNAL STRUCTURE GLOBALDEF UNION TYPE You can dimension the major structure, a minor structure, or any member of the structure: that is, there can be arrays of structures and structures whose members are arrays.
Member names cannot have any of the attributes a major structure can have except for INTERNAL and UNION attributes. You can use the UNION attribute on any member with a level number.
If a structure has the STATIC attribute, the extents of all members (lengths for character- and bit-string variables, dimensions for array variables, and area extents) must be specified with optionally signed decimal integer constants.
4.2.2. Using The UNION Attribute On Structure Declarations
A union is a variation of a structure in which all immediate members occupy the same storage. The UNION attribute (which must be associated with a level number in a structure declaration) declares a union. All immediate members of the union-that is, all members having a logical level number one higher-occupy the same storage. A reference to one member of a union refers to storage occupied by all members of the union. Therefore, a union provides a convenient way to look at a large entity (such as a character string or a bit mask) as a series of smaller entities (such as component character strings or individual flag bits).
A variable declared with the UNION attribute must be a major or minor structure. All members of a union must have a constant size (see Chapter 2, Declarations for format and details).
The UNION attribute is not part of the PL/I General-Purpose Subset; it is provided in ESS implementations of PL/I to give users convenient access to data as it is internally represented. Potential applications of unions might depend on the internal representation of data, and would therefore not be transportable between OpenVMS VAX and OpenVMS Alpha systems. The following example shows unions:
DECLARE 1 CUSTOMER_INFO, ⋮ 2 PHONE_DATA UNION, 3 PHONE_NUMBER CHARACTER (13), 3 COMPONENTS, 4 LEFT_PAREN CHARACTER (1), 4 AREA_CODE CHARACTER (3), 4 RIGHT_PAREN CHARACTER (1), 4 EXCHANGE CHARACTER (3), 4 HYPHEN CHARACTER (1), 4 SPECIFIC_NUMBER CHARACTER (4), 2 ADDRESS_DATA, ⋮
The UNION attribute associated with the declaration of PHONE_DATA signifies that PHONE_DATA's immediate members (PHONE_NUMBER and COMPONENTS) occupy the same storage. Any modification of PHONE_NUMBER also modifies one or more members of COMPONENTS; conversely, modification of a member of COMPONENTS also modifies PHONE_NUMBER. Note, however, that the UNION attribute does not apply to the members of COMPONENTS because they are not immediate members of PHONE_DATA. The members of COMPONENTS occupy separate storage in the normal fashion for structure members.
Unions provide capabilities similar to those provided by defined variables. However, the rules governing defined variables are more restrictive than those governing unions. The following example (for VAX only) demonstrates a use of a union that would not be possible with a defined variable:
DECLARE 1 X UNION, 2 FLOAT_NUM FLOAT BINARY (24), 2 BREAKDOWN, 3 FRAC_1 BIT (7), 3 EXPONENT BIT (8), 3 SIGN BIT (1), 3 FRAC_2 BIT (16);
The union X has two immediate members, FLOAT_NUM (a floating-point variable) and BREAKDOWN. The members of BREAKDOWN are bit-string variables that overlay the storage occupied by FLOAT_NUM and provide access to the individual components of an VAX floating-point value. Assignment to FLOAT_NUM modifies the members of BREAKDOWN and vice versa. For example:
EXPONENT = '0'B; SIGN = '1'B; FLOAT_NUM = FLOAT_NUM + 1;
The first two assignment statements set the exponent and sign fields of FLOAT_NUM to the reserved operand combination; the expression FLOAT_NUM + 1 causes a reserved operand exception to occur.
Note that, unlike the character-string example that precedes it, this example depends on the VAX internal representation of data.
4.2.3. Initializing Structures
You can initialize a structure by giving the INITIAL attribute to its members. Not all members need be initialized. For example:
DECLARE 1 COUNTS, 2 FIRST FIXED BIN(15) INITIAL(0), 2 SECOND FIXED BIN(15), 2 THIRD (5) FIXED BIN(15) INITIAL (5(1));
The first and third members of the structure COUNTS are initialized.
The INITIAL attribute cannot be applied, however, to a major or a minor structure name.
4.2.4. Using Structure Variables in Expressions
You can specify the name of a major or minor structure in an assignment statement only if the source expression and the target variable are identical in size and structure, and all corresponding members have the same data types.
4.2.5. Passing Structure Variables as Arguments
You can pass a structure variable as an argument to another procedure. The relative structuring of the structure variable specified as the argument and the corresponding parameter must be the same. The level numbers do not have to be identical. The following example shows the parameter descriptor for a structure variable:
DECLARE SEND_REC ENTRY (1, 2 FIXED BINARY(31), 2 CHARACTER(40), 2 PICTURE '999V99');
The written argument in the invocation of the external procedure SEND_REC must have the same structure, and its corresponding members must have the same data types.
When structures are passed as arguments, they must match the corresponding parameters. They cannot be passed by dummy argument.
4.2.6. Member Attributes
PL/I supports three member attributes, so named because they apply specifically to the declaration of structure members rather than to the structure as a whole. The member attributes are as follows:
The TYPE attribute
The LIKE attribute
The REFER option
Each is discussed in detail in the following sections.
4.2.6.1. Using the TYPE Attribute
The TYPE attribute copies a scalar, array, or member declaration in a major or minor structure into another scalar, array, or structure variable respectively. The TYPE attribute copies the attributes to the target variable. For structures, the TYPE attribute also copies the logical structuring and member declarations from the major or minor structure to the target variable. TYPE does not copy any storage class or INITIAL attributes or dimensioning (except for dimensioning that is applied to arrays and members) from scalars, arrays, or structures.
Note that the TYPE attribute is a superset of the LIKE attribute. The TYPE attribute is identical to the LIKE attribute when it is used to copy a member declaration in a major or minor structure declaration into another structure variable.
An identifier names the variable to which the declarations for the reference are copied. The reference is the name of a scalar, an array, or a major or minor structure known to the current block. For structures, the identifier must be preceded by a level number. Any attributes that can be used with a structure variable at that level can be used with the identifier. For example, a major structure can specify a storage class and dimensions, and a minor structure can specify dimensions. The following example shows the TYPE attribute:
DECLARE NO_OF_SINGLE_ROOMS FIXED BINARY(31); DECLARE NO_OF_DOUBLE_ROOMS TYPE (NO_OF_SINGLE_ROOMS);
In the previous example, the declaration of NO_OF_DOUBLE_ROOMS uses the TYPE attribute to create a declaration that duplicates the attributes of NO_OF_SINGLE_ROOMS. The declaration of NO_OF_DOUBLE_ROOMS is equivalent to the following:
DECLARE NO_OF_DOUBLE_ROOMS FIXED BINARY(31);
In the next example, the declaration uses the TYPE attribute to create the declaration that duplicates the attributes of BED_SERIAL_NOS:
DECLARE BED_SERIAL_NOS((NO_OF_SINGLE_ROOMS + NO_OF_DOUBLE_ROOMS),2) CHARACTER(12); DECLARE TABLE_SERIAL_NOS TYPE(BED_SERIAL_NOS);
The declaration of TABLE_SERIAL_NOS in the previous example is equivalent to the following:
DECLARE TABLE_SERIAL_NOS((NO_OF_SINGLE_ROOMS + NO_OF_DOUBLE_ROOMS),2) CHARACTER(12);
In the following example, the declaration of NEW_RESER uses the TYPE attribute to create a set of member declarations that duplicate those in RES_DATA:
DECLARE 1 RES_DATA BASED (RPTR), 2 DATE CHARACTER(8), 2 HOTEL_CODE CHARACTER(3), 2 PARTY_NAME, 3 LAST CHARACTER(20), 3 FIRST CHARACTER(10), 2 STAY FIXED BIN(7), 1 NEW_RESER TYPE(RES_DATA), ⋮ GET LIST (NEW_RESER.DATE,NEW_RESER.HOTEL_CODE); ⋮ RES_DATA = NEW_RESER;
The declaration of NEW_RESER in the previous example is equivalent to the following:
DECLARE 1 NEW_RESER, 2 DATE CHARACTER(8), 2 HOTEL_CODE CHARACTER(3), 2 PARTY_NAME, 3 LAST CHARACTER(20), 3 FIRST CHARACTER(10), 2 STAY FIXED BINARY(7);
In the previous example, the members of NEW_RESER are assigned data after that data is validated; the entire contents of NEW_RESER are assigned to RES_DATA. This assignment is possible because the two structures are identical as a result of using the TYPE attribute.
You can use the TYPE attribute to copy a minor structure to a major structure and vice versa; neither the level numbers nor the logical levels must match. For example:
DECLARE 1 PARTY_NAME, 2 LAST CHAR(20), 2 FIRST CHAR(10); DECLARE 1 SPOUSE_NAME TYPE(PARTY_NAME);
Given the declarations in the preceding example, this declaration is equivalent to the following:
DECLARE 1 SPOUSE_NAME, 2 LAST CHAR(20), 2 FIRST CHAR(10);
You can also apply dimensions or, for a major structure, storage-class attributes to a
structure variable declared with the TYPE attribute, as follows:
DECLARE 1 KID_NAMES (10) TYPE(PARTY_NAME);
Or, you can use:
DECLARE 1 DAILY_DATA, 2 DATE CHAR(8), 2 TODAYS_RESERS (NO_OF_RES) TYPE(RES_DATA); ⋮
4.2.6.2. Using the LIKE Attribute
The LIKE attribute copies the member declarations in a major or minor structure declaration into another structure
variable. It copies the logical structuring and member declarations from the major or minor structure to the target
variable, but does not copy any storage-class attributes or dimensioning (except for
dimensioning that is applied to members).
An identifier names the variable to which the declarations in the reference are copied. The reference is the name of a major or minor structure known to the current block. The identifier must be preceded by a level number. Any attributes that can be used with a structure variable at that level can be used with the identifier; for example, a major structure can specify a storage class and dimensions, and a minor structure can specify dimensions.
The following example shows the LIKE attribute:
DECLARE 1 RES_DATA BASED (RPTR), 2 DATE CHARACTER(8), 2 HOTEL_CODE CHARACTER(3), 2 PARTY_NAME, 3 LAST CHARACTER(20), 3 FIRST CHARACTER(10), 2 STAY FIXED BIN(7), 1 NEW_RESER LIKE RES_DATA, ⋮ GET LIST (NEW_RESER.DATE,NEW_RESER.HOTEL_CODE); ⋮ RES_DATA = NEW_RESER;
In the previous example, the declaration of NEW_RESER uses the LIKE attribute to create a set of member declarations that duplicate those in RES_DATA. The declaration of NEW_RESER is equivalent to the following:
DECLARE 1 NEW_RESER, 2 DATE CHARACTER(8), 2 HOTEL_CODE CHARACTER(3), 2 PARTY_NAME, 3 LAST CHARACTER(20), 3 FIRST CHARACTER(10), 2 STAY FIXED BINARY(7);
In the previous example, the members of NEW_RESER are assigned data after that data is validated, the entire contents of NEW_RESER are assigned to RES_DATA. This assignment is possible because the two structures are identical as a result of using of the LIKE attribute.
You can use the LIKE attribute to copy a minor structure to a major structure and vice versa; neither the level numbers nor the logical levels must match. For example:
DECLARE 1 SPOUSE_NAME LIKE PARTY_NAME;
Given the declarations in the preceding example, this declaration is equivalent to the following:
DECLARE 1 SPOUSE_NAME, 2 LAST CHAR(20), 2 FIRST CHAR(10);
You can also apply dimensions or, for a major structure, storage-class attributes to a
structure variable declared with the LIKE attribute:
DECLARE 1 KID_NAMES (10) LIKE PARTY_NAME;
or
DECLARE 1 DAILY_DATA, 2 DATE CHAR(8), 2 TODAYS_RESERS (NO_OF_RES) LIKE RES_DATA; ⋮
4.2.6.3. Using the REFER Option
Use the REFER option to create self-defining based structures. In a based structure, the value of one member is used to determine the size of the storage space allocated for another member of the same structure. You can use the REFER option in a DECLARE statement to specify array bounds, the length of a string, or the size of an area. The format of the REFER option is as follows:
refer-elementREFER (refer-object-reference)
refer-elementAn expression that represents the value assigned to the refer object when the structure is allocated. The refer element must satisfy the following conditions:
It must be an expression that produces a FIXED BINARY(31) value or a value that can be converted to FIXED BINARY (31).
It cannot reference storage in the structure containing the refer element.
refer-object-referenceA reference to a scalar variable. The refer object reference must satisfy the following conditions:
It cannot be a subscripted variable reference.
It cannot be locator qualified.
It must reference a refer object that is a previous member of the structure containing the REFER option.
The refer object is a scalar variable contained by the structure. The refer object must satisfy the following conditions:
It must be a previous member of the structure containing the REFER option, which references the refer object.
It must be scalar; it cannot be dimensioned or a dimensioned array.
It must have a computational data type.
An example of a structure declaration containing the REFER option is as follows:
DECLARE 1 STRUCTURE S BASED(P), 2 I FIXED BINARY(31), 2 A CHARACTER(20 REFER(I));
For the compiler to allocate storage for a based structure, the structure must have a known size. In the example, the initial length for A is taken from the refer element, 20. However, the REFER option permits the size of the structure to change at run time as the value of the refer object (I) changes. After allocation, the length of A is determined by I.
You can have multiple REFER options within a structure.
The following example and figures show storage mapping with the REFER option.
DECLARE 1 S BASED (POINTER), 2 I FIXED BINARY(15), 2 J FIXED BINARY(15), 2 A CHARACTER ((X*2+2) REFER(I)), 2 B(2) CHARACTER (Y REFER(J)); X = 5; Y = 10; ALLOCATE S; S.A = 'ABCDEFGHIJKL'; S.B(1) = '0123456789'; S.B(2) = 'NOW IS THE'; ⋮ END;
When this structure is allocated, the refer elements (X*2+2) and Y are evaluated and used to determine the length of the associated string. The evaluated refer element value (X*2+2) is assigned to the refer object I and Y is assigned to J. Thereafter, the sizes of strings A and B are determined by the value of the refer objects I and J.
Storage for the previous structure is shown in Figure 4.2, “Storage of Structure with REFER Option”.
If the refer object I is assigned the value 6 and the refer object J is assigned the value 4, the resulting storage is remapped as shown in Figure 4.3, “Remapped Storage of Structure with REFER Option”.
Note
PL/I does not restrict the use of the REFER option within structure declarations: therefore, exercise caution in its use.
If you change a value that causes the size of one or more structure members to decrease, then some storage at the end of the allocated storage will become inaccessible for future reference.
If the scalar variable (the refer object) does not satisfy the following criteria, the results are undefined:
It must not be assigned a value that is less than 0 or greater than the refer element value used for structure allocation.
It must have the value used for allocation, if the structure is freed.
The following rules apply to structures containing the REFER option:
A structure containing the REFER option cannot be the target of a LIKE reference.
When a based structure is allocated, the order in which the refer elements are selected for evaluation is undefined.
When a based structure is allocated, the order in which the refer objects are selected for initialization is undefined.
4.2.7. Structure-Qualified References
To refer to a structure in a program, you use the major structure name, minor structure names, and individual member names. Member names need not be unique even within the same structure. To refer to the name of a member or minor structure, you must ensure only that the reference uniquely identifies it. You can qualify the variable name by preceding it with the name or names of higher-level (lower-numbered) variables in the structure; names in this format, called a qualified reference, must be separated by periods (.).
The following sample structure definition shows the rules for identifying names of variables within structures:
DECLARE 1 STATE, 2 NAME CHARACTER (20), 2 POPULATION FIXED (10), 2 CAPITAL, 3 NAME CHARACTER (30), 3 POPULATION FIXED (10,0), 2 SYMBOLS, 3 FLOWER CHARACTER (20), 3 BIRD CHARACTER (20);
The rules for selecting and specifying variable names for structures are as follows:
The name of the major structure is subject to the rules for the scope of variables in a program.
You can qualify the name of any minor structure or member in a structure by the names of higher-level members in the structure. The variable names must be specified from left to right in order of increasing level numbers separated by periods. The members of the previous sample, completely qualified, are as follows:
STATE.NAME
STATE.POPULATION
STATE.CAPITAL.NAME
STATE.CAPITAL.POPULATION
STATE.SYMBOLS.FLOWER
STATE.SYMBOLS.BIRD
Names of minor structures or members within structures do not have to be qualified if they are unique within the scope of the name. The following names in the sample structure can be referred to without qualification (so long as there are no other variables with these names):
CAPITAL
SYMBOLS
FLOWER
BIRD
You can omit intermediate qualification names if the reference remains unambiguous. The following references to members in the sample structure are valid:
STATE.FLOWER
STATE.BIRD
If a name is ambiguous, the compiler cannot resolve the reference and issues a message. In the example, the names POPULATION and NAME are ambiguous.
You can specify the name of a major or minor structure in an assignment statement only if the source expression and the target variable are identical in size and structure, and all corresponding members have the same data types.
4.3. Arrays of Structures
An array of structures is an array whose elements are structures. Each structure has identical logical levels, minor structure names, and member names and attributes. For example, a structure STATE can be declared an array:
DECLARE 1 STATE (50), 2 NAME CHARACTER (20) VARYING, 2 POPULATION FIXED (31), 2 CAPITAL, 3 NAME CHARACTER (30) VARYING, 3 POPULATION FIXED (31), 2 SYMBOLS, 3 FLOWER CHARACTER (20), 3 BIRD CHARACTER (20);
A member of a structure that is an array inherits the dimensions of the structure. For example, the member CAPITAL.NAME of the structure STATE inherits the dimension 50. You must use a subscript whenever you refer to the variable CAPITAL.NAME, as in the following example:
PUT LIST (CAPITAL.NAME(I)) ;
A subscript for a member of a structure that is an array element can appear following any name within a qualified reference. For example, all of these references are equivalent:
STATE(10).CAPITAL.NAME STATE.CAPITAL(10).NAME STATE.CAPITAL.NAME(10)
4.3.1. Arrays of Structures that Contain Arrays
A structure that is defined with a dimension can have members that are arrays. For example:
DECLARE 1 STATE (50), 2 AVERAGE_TEMPS(12) FIXED DECIMAL (5,2), ⋮
In this example, the elements of the array STATE are structures. At the second level of the hierarchy of each structure, AVERAGE_TEMPS is an array of 12 elements. Because AVERAGE_TEMPS inherits the dimension of STATE, any of AVERAGE_TEMPS's elements must be referred to by two subscripts:
The first subscript references an element in STATE.
The second subscript references an element in AVERAGE_TEMPS.
These subscripts can appear following any name in the qualified reference. For example:
STATE(3).AVERAGE_TEMPS(4) STATE.AVERAGE_TEMPS(3,4)
These references are equivalent.
Note the following rules for specifying subscripts for members of structures containing arrays:
The number of subscripts specified for any member must include any dimensions inherited from a major or minor structure declaration, as well as those specified for the member itself.
The subscripts that refer to a member of a structure in an array do not have to follow immediately the name to which they apply. However, the order of subscripts must be preserved.
The total number of dimensions, including the inherited dimensions, must not exceed eight.
4.3.2. Connected and Unconnected Arrays
A connected array is one whose elements occupy consecutive locations in storage. For example:
DECLARE NEWSPAPERS (10) CHARACTER (30);
In storage, the 10 elements of the array NEWSPAPERS occupy 10 consecutive 30-byte units. Thus, NEWSPAPERS is a connected array.
A connected array is valid as the target of an assignment statement, as long as the source expression is a similarly dimensioned array or a single scalar value. The top diagram in Figure 4.4, “Connected and Unconnected Arrays” shows the storage of a connected array.
In an unconnected array, the elements do not occupy consecutive storage locations. The bottom diagram in Figure 4.4, “Connected and Unconnected Arrays” shows the storage of an unconnected array. An unconnected array is not valid in an assignment statement or as the source or target of a record I/O statement. A structure with the dimension attribute always results in unconnected arrays. When a structure is dimensioned, each member of the structure inherits the dimensions of the structure and becomes, in effect, an array. For example:
DECLARE 1 STATE (50), 2 NAME CHARACTER (20) VARYING, 2 POPULATION FIXED (31);
In this example, the members NAME and POPULATION of the major structure STATE inherit the dimension 50 from the major structure. When PL/I allocates storage for a structure or a dimensioned structure, each member is allocated consecutive storage locations; thus, the elements of the arrays NAME and POPULATION are not connected.
4.4. Internal Representation of Aggregate Data
Structures can be unaligned or naturally aligned. When a structure is unaligned, each of its members (except for unaligned bit string members) is aligned on a byte boundary. Unaligned bit-string members are bit aligned. In an array of unaligned structures (which contain members other than unaligned bit strings), each structure is aligned on a byte boundary. In an array of unaligned structures that contain only unaligned bit strings, the array elements are bit aligned.
When a structure is naturally aligned, each of its members is aligned as described in Table 4.2, “Natural Alignment for Structure Members”. In an array of naturally aligned structures, each structure is aligned on the boundary that is the maximum alignment of its members.
The alignment you select is determined by the compile-time /NOALIGN or /ALIGN switch. The /NOALIGN switch (the default) produces unaligned structures. The /ALIGN switch produces aligned structures as described in Table 4.2, “Natural Alignment for Structure Members”.
| Data Type | Precision | Alignment |
|---|---|---|
| FIXED BINARY(p) | p <= 7 | byte |
| FIXED BINARY(p) | 7 < p <= 15 | word |
| FIXED BINARY(p) | p > 15 | longword |
| FIXED DECIMAL(p,q) | word | |
| FLOAT BINARY(p) | p <= 24 | longword |
| FLOAT BINARY(p) | 24 < p <= 53 | quadword |
| FLOAT BINARY(p) | p > 53 | octaword |
| FLOAT DECIMAL(p) | p <= 7 | longword |
| FLOAT DECIMAL(p) | 7 < p <= 15 | quadword |
| FLOAT DECIMAL(p) | p > 15 | octaword |
| CHAR (UNALIGNED) | byte | |
| CHAR ALIGNED BYTE | byte | |
| CHAR VARYING | word | |
| BIT (UNALIGNED) | bit | |
| BIT ALIGNED | longword | |
| POINTER | longword | |
| LABEL | quadword | |
| ENTRY | quadword | |
| FILE | longword | |
| STRUCTURE | maximum of members | |
| PICTURE | byte | |
| OFFSET | longword |