【実務・中級編】DEFINED属性によるメモリ領域の再定義(REDEFINES) – PL/Iの基本構文とデータ制御実践ガイド

「メモリを喰らう」感覚を忘れるな:PL/IにおけるDEFINED属性とメモリ再定義の深淵

若手エンジニアからよく受ける質問に、「ポインタを使わずに、一つのレコードを別の型として扱うにはどうすればいいか?」というものがある。COBOLの`REDEFINES`に慣れた連中が、PL/Iのコードを読んで「この`DEFINED`ってやつ、本当に安全なのか?」と疑心暗鬼になる気持ちはよくわかる。

結論から言おう。PL/Iの`DEFINED`属性は、COBOLのそれよりも遥かに強力で、かつ一歩間違えば「真夜中のABEND」を誘発する諸刃の剣だ。今日は、基幹システムのバッチ処理で避けては通れない、メモリ再定義の勘所を伝授する。

なぜ今、DEFINED属性が必要なのか

VSAMファイルから読み込んだ固定長レコードを、ある時は「ヘッダー情報」として扱い、ある時は「詳細明細」として解析したい。そんな時、わざわざストレージをコピーしてデータを詰め直すような無駄な処理は書かない。メインフレームの限られたメモリリソースを無駄遣いするな。

`DEFINED`を使えば、一つの物理メモリ領域に対して、異なる構造の「窓」を当てはめることができる。これにより、データのコピーなしに型変換(あるいは解釈の変更)が可能になるのだ。

実践:DEFINEDによるメモリの再定義

以下の例を見てほしい。一つの80バイトの領域に対し、全体を扱う構造体と、特定のフィールドを別形式で参照する定義を共存させている。

1
/ ————————————————————- /
/ プログラム名:REDEF_SAMPLE /
/ 目的:DEFINED属性を利用したメモリ共用の実践 /
/ ————————————————————- /
REDEF_SAMPLE: PROCEDURE OPTIONS(MAIN);

/ 共通の物理領域として80バイトを確保 /
DCL RAW_BUFFER CHAR(80) STATIC;

/ その領域を別の構造体として再定義する(ポジショナル定義) /
DCL 1 HEADER_REC DEFINED(RAW_BUFFER),
3 RECORD_TYPE CHAR(2),
3 DATA_LEN FIXED BIN(15);

DCL 1 DETAIL_REC DEFINED(RAW_BUFFER),
3 FILLER CHAR(2), / 先頭2バイトはHEADERと共通 /
3 ACCOUNT_NO CHAR(10),
3 AMOUNT FIXED DEC(9,2);

/ 処理ロジック例 /
RAW_BUFFER = ‘A100001234567890000050000’; / テストデータ投入 /

/ ヘッダーとして解釈 /
PUT SKIP LIST(‘REC_TYPE:’ || HEADER_REC.RECORD_TYPE);

/ 同じメモリを明細として解釈 /
IF HEADER_REC.RECORD_TYPE = ‘A1’ THEN DO;
PUT SKIP LIST(‘ACC_NO:’ || DETAIL_REC.ACCOUNT_NO);
PUT SKIP LIST(‘AMT:’ || TRIM(CHAR(DETAIL_REC.AMOUNT)));
END;

END REDEF_SAMPLE;

現場で「死なない」ための3つの鉄則

コードを見れば簡単そうだが、大規模なバッチ改修でこれを扱う際には、以下の3点を叩き込んでおいてほしい。

1. アライメントの不一致は「境界エラー」の温床

`DEFINED`は物理的なオフセットを厳密に管理する。もし`FIXED BIN(31)`などを再定義の途中に挟む場合、コンパイラが自動的に挿入する「パディング(詰め物)」に注意しろ。特に`ALIGNED`属性を明示しない場合、コンパイラが最適化のために勝手に境界調整を行うことがある。`UNALIGNED`属性を明示的に指定し、メモリレイアウトを完全に制御するのがプロの作法だ。

2. ONユニットによる例外監視を怠るな

メモリを強制的に型変換するということは、無効なビットパターンがデータとして解釈されるリスクを伴う。特に`FIXED DEC`(パック10進数)として再定義した領域に不正な文字データが入っていた場合、演算時に`CONVERSION`例外が発生する。
`ON CONVERSION`ユニットを適切に配置し、異常値が混入した際のダンプ取得とログ出力を自動化しておくのが、夜間バッチを安定させるための「守り」だ。

3. 「暗黙の定義」は悪魔の誘惑

`DEFINED`には、特定の要素を指定する形式の他に、`POSITION`を指定する方法がある。
`DCL B CHAR(10) DEFINED(A) POSITION(5);`
この書き方は便利だが、ベースとなる変数`A`の構造が変わった瞬間、バグの温床になる。複雑な構造体に対して多用するのは避けろ。保守担当者が泣くことになる。

アーキテクトからのアドバイス

最近のマイグレーションプロジェクトでは、JavaやC#への移行検討がなされることもある。しかし、メモリをバイト単位で制御し、CPUサイクルを極限まで削り出すこのPL/Iの「職人気質」なスタイルは、やはりメインフレームならではの美学だ。

`DEFINED`属性を使う際は、「自分が今、メモリのどのオフセットを、何の型として解釈しようとしているのか」を常に頭の中でシミュレートするんだ。もし不安なら、`STORAGE`関数を使って、アドレスを確認するコードをデバッグ用に入れてもいい。

次にコンパイルを通す時、コンパイラの警告一つ一つに意味があることを思い出してくれ。それが、君が一人前のメインフレーマーになるための通過儀礼だ。

質問があればいつでも来い。ただし、マニュアルに書いてあることではなく、マニュアルの「行間」にあるトラブルの話を期待しているぞ。

タイトルとURLをコピーしました