制御変数

CONTROLLED 属性は、記憶域の割り当ておよび解放がブロック境界に関係なく世代別に動的に行われる変数を指定します。制御変数は、ALLOCATE 文で指定された場合にのみ割り当てられます。割り当てられた制御変数は、FREE 文で指定されるまで割り当てられたまま維持されます。

制御変数の参照では、最新の ALLOCATE 文で作成された世代の変数が参照されます。FREE 文は、最新の世代の制御変数を解放し、前の世代を公開します。次に例を示します。

DECLARE STACK (STACK_ITEM_SIZE) CHAR(32) CTL; 
DECLARE STACK_ITEM_SIZE FIXED BIN;
DECLARE I FIXED BIN;

   /* Push a first stack item. */ 
   STACK_ITEM_SIZE = 4;
   ALLOCATE STACK;

   DO I = 1 TO STACK_ITEM_SIZE; 
      STACK(I) = CHAR(I) || '_1'; 
   END;
   
   /* Push a second stack item. */ 
   STACK_ITEM_SIZE = 3;
   ALLOCATE STACK;

   DO I = 1 TO STACK_ITEM_SIZE; 
      STACK(I) = CHAR(I) || '_2'; 
   END;

   PUT SKIP LIST ('Print second item on the stack'); 
   PUT SKIP LIST (STACK);

   /* Pop the last element on the stack. */ 
   FREE STACK;


   PUT SKIP LIST ('Print first item on the stack'); 
   PUT SKIP LIST (STACK);

   /* Pop the last element on the stack. 
      The stack is empty now. */
   FREE STACK;

CONTROLLED 変数の宣言では、範囲指定にアスタリスク (*) を使用できます。この場合、変数の割り当て時に、ALLOCATE 文で属性を指定して明示的な範囲を指定できます。また、CONTROLLED 変数の宣言で明示的な範囲 (定数または変数) を指定することもできます。これらの明示的な範囲は、ALLOCATE 文で属性を指定してオーバーライドできます。

ALLOCATE 文の属性の指定でも、範囲指定にアスタリスクを使用することができます。この場合、同じ CONTROLLED 変数の前の割り当てで使用された対応する範囲がコピーされます。ALLOCATE 文で変数のいずれかの次元についてアスタリスクを指定する場合は、その変数のすべての次元に対して同じように指定する必要があります。

レベル番号を指定する場合は、ALLOCATE 文の最初の変数の level-num を 1 に指定し、レベル 1 の構造体として宣言する必要があります。この場合、その構造体のすべてのメンバーを構造体の宣言のとおりに ALLOCATE 文に含める必要があります。

ALLOCATE 文で指定された境界、長さ、サイズ、または初期値は、変数の宣言で指定された対応する属性をオーバーライドします。ALLOCATE 文での境界、長さ、またはサイズの指定にはアスタリスクを使用できます。この場合、境界、長さ、またはサイズは、その CONTROLLED 変数の現在の世代から取得されます。ALLOCATEス 文で指定された境界、長さ、サイズ、または初期値に、割り当てる変数への参照が含まれている場合、現在の世代の変数が参照されます。

ALLOCATE 文の実行時の境界、長さ、サイズ、および初期値の式の評価は、PL/I の参照の通常の規則に従って行われ、論理的に実行できる必要があります (循環性があってはなりません)。評価が前の世代の変数の存在に依存する場合は、該当する世代が先に割り当てられている必要があります。

制御変数を使用すると、最新の世代のみをプログラムで利用できるため、スタックの実装が簡単になります。ALLOCATE 文はプッシュ操作に相当し、FREE 文はポップ操作に相当します。制御変数の使用可能な世代の数の判別には、ALLOCATION 組み込み関数が使用されます。

DECLARE 1 A CONTROLLED,
     2 B CHAR(*),
     2 C CHAR(20),
     2 D(2) FIXED BIN(31),
     2 E(4,4) CHAR(*),
     2 F FIXED BIN(31);
...
ALLOCATE 1 A, 2 B CHAR(12), 2 C, 2 D(6), 2 E CHAR(3), 2 F INIT(128);
...
ALLOCATE 1 A, 2 B CHAR(12), 2 C CHAR(10), 2 D(D(1)), 2 E CHAR(3), 2 F;

この例では、構造体 A の最初の割り当てで、メンバー B の長さが 12、配列メンバー D の次元が宣言の (2) ではなく (6)、配列メンバー E の次元が宣言の (4,4) で要素の長さが 3、メンバー F の初期値が 128 になっています。2 番目の世代の A の割り当てでは、メンバー C の長さが 10 だけになり、配列メンバー D の次元が最初の世代の A に格納される値 D(1) になっています。

DECLARE X(*,*) CONTROLLED;
ALLOCATE X(3,7);
ALLOCATE X(*,*);

配列 X の 2 番目の割り当てでは、境界が前の割り当てから取得され、暗黙的に (3,7) になります。

CONTROLLED のパラメーターについては、ENTRY 宣言のパラメーター記述子に CONTROLLED を明示的に記述する必要があります。これは PL/I 言語の仕様によるものです。たとえば、次の 2 つのプロシージャをコンパイルしてリンクするとします。

t.pl1
 TSTENTM: PROC OPTIONS(MAIN);

   DCL   N  CHAR(10) CONTROLLED;

   DCL  TSTENT ENTRY(CHAR(10));

   ALLOCATE N;
   N = 'CONTROLLED';

   CALL TSTENT(N);
 END;

t1.pl1
 TSTENT:  PROC(N);
   DCL   N  CHAR(10) CONTROLLED;
   PUT SKIP LIST ('N = ',N);
   RETURN;
 END;

これを実行すると、次のような結果になります。

*** Condition ERROR raised
*** Unhandled condition SIGSEGV at PC=004011AE
N =

これを回避するには、ENTRY 宣言に制御属性を追加します。

DCL  TSTENT ENTRY(CHAR(10)CTL);