DocumentationEndless PL/I for OpenVMSReference Manual  ·  ESS-PFVRM-001

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:

DECLARE identifier [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 ...];
identifier

A valid PL/I identifier to be used as the name of the array.

bound-pair

A 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.

attribute

One 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.

Table 4.1. Specifying Array Dimensions
Bound PairsExamples

ARRAY_NAME (bound)

A single value specifies:

  • That the array has a single dimension.

  • That the number of elements in the dimension is the bound (that is, 6).

  • That the value specified is the high bound, which is the largest numbered element. By default, the low bound is 1.

DECLARE VERBS (6) CHARACTER (12) ;
ARRAY_NAME (low-bound:high-bound)

A single range of values specifies:

  • That the array has a single dimension.

  • That the number of elements in the dimension is (high-bound) - (low-bound) + 1.

  • That the first value specified is the low bound and the second value specified is the high bound.

DECLARE TEMPERATURES (-60:120) ;

ARRAY_NAME (bound1,bound2,...)

A list of values specifies:

  • That the array is multidimensional. Each bound value represents a dimension in the array.

  • The extent of each dimension. Each bound defines the number of elements in a dimension.

  • The high-bound value of each dimension. The low-bound value of each dimension defaults to 1.

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:

  • That the array is multidimensional. Each range of values represents a dimension of the array (ranges can be intermixed with single-bound specifications).

  • The extent of each dimension.

  • The low-bound and high-bound values of each dimension.

DECLARE WINDOWS (1:10,-2:32) FIXED;
DECLARE HISTORIES (10,30:102,50) ...

ARRAY_NAME (*,...)

Asterisk extents specify:

  • The number of dimensions in the array. Each asterisk indicates a dimension.

  • That the extent of each dimension will be defined by the actual argument passed to the procedure when it is invoked.

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.

Figure 4.1. Specifying Elements of an Array
Specifying Elements of an Array

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-factor

An 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-constant

Any arithmetic constant whose data type is valid for conversion to the data type of the array.

scalar-reference

A reference to any scalar variable or to the NULL built-in function.

scalar-expression

Any 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:

  • DIMENSION returns the number of elements in a given dimension.

  • HBOUND returns the value of the upper bound of the array in a given dimension.

  • LBOUND returns the value of the lower bound of the array in a given dimension.

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:

    AUTOMATICGLOBALREF
    BASEDINTERNAL
    CONTROLLEDREADONLY
    DEFINEDSTATIC
    EXTERNALSTRUCTURE
    GLOBALDEFUNION
    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.

In a union, the same data can only be initialized once.

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-element REFER (refer-object-reference)
refer-element

An 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-reference

A 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”.

Figure 4.2. Storage of Structure with REFER Option
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”.

Figure 4.3. Remapped Storage of Structure with REFER Option
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.

Figure 4.4. Connected and Unconnected Arrays
Connected and Unconnected Arrays

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”.

Table 4.2. Natural Alignment for Structure Members
Data TypePrecisionAlignment
FIXED BINARY(p)p <= 7byte
FIXED BINARY(p)7 < p <= 15word
FIXED BINARY(p)p > 15longword
FIXED DECIMAL(p,q) word
FLOAT BINARY(p)p <= 24longword
FLOAT BINARY(p)24 < p <= 53quadword
FLOAT BINARY(p)p > 53octaword
FLOAT DECIMAL(p)p <= 7longword
FLOAT DECIMAL(p)7 < p <= 15quadword
FLOAT DECIMAL(p)p > 15octaword
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