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

 * 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
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
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
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.
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;