Software Engineers Handbook/Language Dictionary/PLI/storage classes

Note: This article requires basic knowledge of PL/I which can be found in Software Engineers Handbook/Language Dictionary/PLI.

PL/I offers 4 different storage classes:

  • STATIC
  • AUTOMATIC (abbr: AUTO)
  • CONTROLLED (abbr: CTL)
  • BASED

Simplified spoken static and automatic variables will be allocated and initialised "by the machine",
controlled and based variables have to be managed "by the program(mer)".

Static and Automatic Variables edit

Static variables will be allocated and initialised at program start, they remain in storage until program termination[1].
As a consequence of this a static variable which is declared local within a procedure will not loose its value between successive invocations of this procedure.

Automatic variables which are declared global behaves like static variables.

Automatic variables which are declared local within a procedure will be allocated and initialised at every invocation of this procedure, their storage will be released if this procedure terminates.
Local variables are automatic by default[2].

example: proc options ( main );
   call static_memory ( 7 );
   call static_memory ( 0 );   /* output: 7 */
   call   auto_memory ( 7 );
   call   auto_memory ( 0 );   /* output: 1 */
static_memory: proc ( parm );
dcl   parm     bin fixed (15);
dcl   memory   bin fixed (15)   init ( 1 )   STATIC;
   if parm = 0 then
      put skip list ( memory );
   else
      memory = parm;
end static_memory;
auto_memory: proc ( parm );
dcl   parm     bin fixed (15);
dcl   memory   bin fixed (15)   init ( 1 );
   if parm = 0 then
      put skip list ( memory );
   else
      memory = parm;
end auto_memory;
end example;

Controlled Variables edit

If a variable is declared as Controlled the declaration only describes the structure of the variable but no storage allocation will be done automatically.

The acquirement of storage for a controlled variable must be done with a ALLOCATE statement (abbr: ALLOC)[3], the deallocation has to be done with a FREE statement[4].

A controlled variable may be allocated multiple but only the actual generation of it can be directly accessed.

example: proc options ( main );
dcl   number   bin fixed (15) CONTROLLED;
   ALLOCATE number;            /* create the 1st generation of number */
   number = 111;
   put skip list ( number );   /* output: 111 */
   ALLOCATE number;            /* create the 2nd generation of number */
   number = 222;
   put skip list ( number );   /* output: 222 */
   FREE number;                /* now 1st generation becomes actual again */
   put skip list ( number );   /* output: 111 */
   FREE number;                /* now the value of number will be undefined */
end example;

Local variables in a procedure which are controlled remains allocated between successive invocations of this procedure.

example: proc options ( main );
   call sub_proc ( 333 );
   call sub_proc ( - 1 );   /* output: 333 */
sub_proc: proc ( num_val );
dcl   num_val   bin fixed (15);
dcl   num_ctl   bin fixed (15) CTL;
   if num_val >= 0 then
      do;
         alloc num_ctl;
         num_ctl = num_val;
      end;
   else 
      do;
         put skip list ( num_ctl );
         free num_ctl;
      end;
end sub_proc;
end example;

Adjustable Bounds etc. edit

  • Every declared bound, length or size of a controlled variable may be overwritten in the allocate statement.
  • Using asterisks notation in a declaration forces explicid definition in the 1st allocate statement.
  • Using asteriks notation in an allocate statement inherits the values of the actual generartion.
example: proc options ( main );
dcl   stringlist (3)   char (10)   controlled;
dcl   single_string    char (*)    controlled;
dcl   1 structure                  controlled,
        2 string_1     char (10),
        2 string_2     char (10);
dcl   allocation       builtin;
   allocate stringlist;                 /* 1st generation: 3 strings of length 10 */
   allocate stringlist (5);             /* 2nd generation: 5 strings of length 10 */
   allocate stringlist (5) char (20);   /* 3rd generation: 5 strings of length 20 */
   allocate stringlist (*) char (*);    /* 4th generation: like 3rd generation    */
   do while ( allocation ( stringlist ) > 0 ); free stringlist; end;
   allocate single_string char (50);    /* Explicid value must be given because declaration contains asteriks */
   free single_string;
   allocate   structure;                /* 1st generation: length ( string_1 ) = length ( string_2 ) = 10 */
   allocate 1 structure,                /* 2nd generation with ...     */
              2 string_1   char (70),   /*    length ( string_1 ) = 70 */
              2 string_2   char (80);   /*    length ( string_2 ) = 80 */
   do while ( allocation ( structure ) > 0 ); free structure; end;
end example;

Based Variables edit

If a variable is declared as Based the declaration only describes the structure of the variable but no storage allocation will be done automatically.

  • The acquirement of storage for a based variable may be done with a ALLOCATE statement (abbr: ALLOC)[3], the deallocation may be done with a FREE statement[4].
  • Alternatively a based variable may be mapped to still existing storage.

Mapping to Existing Storage edit

example: proc options ( main );
dcl   a_char      char (03);
dcl   b_char      char (03)   based ( addr ( a_char ) );
dcl   c_char      char (03)   based ( c_pointer       );
dcl   d_char      char (03)   based ;
dcl   x_char      char (03)   based ( x_pointer );
dcl   y_char      char (03)   based ( y_pointer );
dcl   z_char      char (03)   based ;
dcl   c_pointer   pointer;
dcl   x_pointer   pointer;
dcl   y_pointer   pointer;
dcl   addr        builtin;
   a_char = 'AAA';                      /* now a_char has the value 'AAA' */
   b_char = 'BBB';                      /* now a_char has the value 'BBB' */
   c_pointer = addr ( a_char );
   c_char = 'CCC';                      /* now a_char has the value 'CCC' */
   addr ( a_char ) -> d_char = 'DDD';   /* now a_char has the value 'DDD' */
   allocate x_char;                     /*================================*/
   x_char = 'XXX';                      /* now x_char has the value 'XXX' */
   y_pointer = x_pointer;
   y_char = 'YYY';                      /* now x_char has the value 'YYY' */
   addr ( y_char ) -> z_char = 'ZZZ';   /* now x_char has the value 'ZZZ' */
end example;
/* The following procedure gets a char_var variable "string"      */
/* as by-reference parameter and trims all spaces from the right. */
/* Structure of a char_var variable:                         */
/*    2 bytes containing the string length as bin fixed (15) */
/*    followed by the characters                             */
right_trim: proc ( string );
dcl   string                 char (*) varying;
dcl   s_length               bin fixed (15)   based ( addr ( string ) );
dcl   s_ch ( - 1 : 32767 )   char (01)        based ( addr ( string ) );
   do while ( s_ch ( s_length ) = ' ' );
      s_length = s_length - 1;
   end;
end right_trim;

Allocating edit

In the following example the ALLOC statement ...

  • acquires storage for the variable B_VAR
  • stores the address of the aquired storage in B_PTR
example: proc options ( main );
dcl   B_VAR   char (25)   BASED ( B_PTR );
dcl   B_PTR   pointer;
   ALLOC B_VAR;
   ............
   FREE B_VAR;
end example;

A based variable may be allocated multiple times, every generation can be directly accessed through its individuell pointer.

example: proc options ( main );
dcl   1 first_item,
        2 value       char (01),
        2 next        pointer    init ( null );
dcl   1 last_item     like first_item   based ( last_ptr );
        /*           "like" means: last_item has the same structure as first_item */
dcl   last_ptr        pointer;
dcl   ( addr , null )  builtin;
   last_ptr = addr ( first_item );
   call append_to_simple_list ( '1' );
   call append_to_simple_list ( '2' );
   call append_to_simple_list ( '3' );
   call append_to_simple_list ( '4' );
   put skip list ( fifo_of_simple_list );   /* output: '1234' */
   call free_simple_list;
append_to_simple_list: proc ( item_value );
dcl   item_value     char (01);
dcl   1 alloc_item   like first_item   based ( alloc_ptr );
dcl   alloc_ptr      pointer;
   last_item.value = item_value;
   ALLOCATE alloc_item;
   last_item.next = alloc_ptr;
   last_ptr = alloc_ptr;
   last_item.next = null;
end append_to_simple_list;
fifo_of_simple_list: proc returns ( char (20) varying );
dcl   list_sum      char (20) varying;
dcl   1 read_item   like first_item   based ( read_ptr );
dcl   read_ptr      pointer;
   list_sum = '';
   read_ptr = addr ( first_item );
   do while ( read_item.next ¬= null );
      list_sum = list_sum || read_item.value;
      read_ptr = read_item.next;
   end;
   return ( list_sum );
end fifo_of_simple_list;
free_simple_list: proc;
dcl   1 free_item   like first_item   based ( free_ptr );
dcl   free_ptr      pointer;
dcl   next_ptr      pointer;
   next_ptr = first_item.next;
   do while ( next_ptr ¬= null );
      free_ptr = next_ptr;
      next_ptr = free_item.next;
      FREE free_item;
   end;
   first_item.next = null;
end free_simple_list;
end example;

Allocating with Adjustable Bounds etc. edit

A based variable may be a self-defining structure, i.e. a structure for which values for bound, length or size must be defined in the allocate statement.

To make e.g. a bound adjustable its fixed value in the declaration has to replaced with

expr REFER ( structure_var )
  • expr is a expression which will be calculated at allocation time and determines the storage value.
  • structure_var is a variable within the based structure which stores the calculated value.
example: proc options ( main );
dcl   1 based_stru,
        2 string_count    bin fixed (15),
        2 string_length   bin fixed (15),
        2 list ( alloc_count REFER ( string_count ) )
                          char ( alloc_length REFER ( string_length ) );
dcl   alloc_count         bin fixed (15);
dcl   alloc_length        bin fixed (15);
dcl   length              builtin;
   alloc_count  =  5;
   alloc_length = 72;
   allocate based_stru;                                  /* now list contains 5 strings of length 72 */
   put skip list ( based_stru.string_length );           /* output will be 72, the stored calculated value.       */
   put skip list ( length ( based_stru.list ( 1 ) ) );   /* output will be 72, the length of an allocated string. */
end example;
example: proc options ( main );
dcl   1 based_stru,
        2 rows       bin fixed (15),
        2 columns    bin fixed (15),
        2 matrix   ( alloc_val * 2 REFER ( rows    ) ,
                     alloc_val + 5 REFER ( columns )   )
                     bin fixed (15);
dcl   alloc_val      bin fixed (15);
   alloc_val = 10;
   allocate based_stru;   /* now matrix contains 20 rows and 15 columns */
end example;

Notes edit

  • [1] local static variables in fetched procedures will only be allocated between the fetch and the release statement.
  • [2] in PL/I default values may be altered using the DEFAULT statement.
  • [3] both controlled and based variables can be allocated in the same statement (e.g.: alloc ctl_var_1 (20), ctl_var_2, based_var;).
  • [4] both controlled and based variables can be freed in the same statement (e.g.: free ctl_var_1, ctl_var_2, based_var;).