【PL/I深掘り】DEFINED属性による「メモリ・オーバーレイ」の魔術と、現場で嵌らないための鉄則
メインフレームの現場で長年コードを追いかけていると、時折「なぜこの変数は、わざわざ別の名前で定義されているのか?」と首を傾げるようなコードに出会うことがあるだろう。PL/Iの`DEFINED`属性は、まさにそんな「古き良き(そして時に危険な)魔法」の一つだ。
今日は、VSAMのレコード処理や、複雑なバッチバッファの解析で避けて通れない`DEFINED`属性の本質と、現場で事故を起こさないための作法について語ろうと思う。
—
1. DEFINED属性とは何か:メモリの「別名(Alias)」を作る技術
`DEFINED`属性は、一言で言えば「既存の変数領域を、別の名前(および別の型)で参照する」ための機能だ。C言語の共用体(union)に近いが、PL/Iのそれはもっと泥臭く、そして強力だ。
例えば、レコードの先頭固定部分は共通だが、レコードタイプによって後半の構造が変わるような場合、あるいは単一の物理メモリ領域を複数の形式で解釈したい場合、この属性は非常に有用だ。
基本的なコード例
まずは、もっともシンプルな「部分参照」の例を見てほしい。
1
/ 構造体のメモリを、別の形式で解釈する例 /
DCL 1 MASTER_REC,
3 KEY_FIELD CHAR(8),
3 DATA_FIELD CHAR(20);
/ MASTER_RECの先頭8バイトを、KEY_PARTとして再定義 /
DCL KEY_PART CHAR(8) DEFINED (MASTER_REC.KEY_FIELD);
/ KEY_PARTを操作すれば、MASTER_REC.KEY_FIELDも連動して変化する /
KEY_PART = ‘ABC00123’;
PUT SKIP LIST (MASTER_REC.KEY_FIELD); / ‘ABC00123’ が出力される /
—
2. 実務で遭遇する「VSAMレコードの再定義」
基幹システムのオンラインプログラムや大バッチでは、VSAMのレコードレイアウトを読み込む際、`DEFINED`が多用される。特に、「共通ヘッダー+可変データ」のレイアウトを扱う際に、冗長なMOVEを省く目的で使われることが多い。
ここで重要なのは、「位置合わせ(アライメント)」と「データ型」の整合性だ。いい加減な定義をすると、意図しないオフセットでメモリが書き換えられ、デバッグ地獄に落ちることになる。
1
/ VSAMバッファを定義 /
DCL VSAM_BUFFER CHAR(100);
/ バッファの一部を構造体としてオーバーレイ /
DCL 1 RECORD_LAYOUT DEFINED (VSAM_BUFFER),
3 REC_TYPE CHAR(1), / 先頭1バイト /
3 REC_BODY CHAR(99); / 残り99バイト /
/ REC_TYPEによって処理を分岐させる標準的なロジック /
READ FILE(VSAM_FILE) INTO (VSAM_BUFFER);
SELECT (RECORD_LAYOUT.REC_TYPE);
WHEN (‘A’) CALL PROCESS_TYPE_A;
WHEN (‘B’) CALL PROCESS_TYPE_B;
OTHERWISE CALL HANDLE_ERROR;
END;
—
3. 注意点:ここを理解していないとバグの温床になる
`DEFINED`を使う際、ベテランの私が必ず若手に釘を刺すのは以下の3点だ。
- 境界整列(Alignment)の問題:
`ALIGNED`属性と`UNALIGNED`属性の組み合わせには注意が必要だ。`DEFINED`された変数が、元となる変数の境界と一致していないと、コンパイラが余計なパディングを挿入したり、アクセス違反でアベンドしたりする。特に`FIXED BIN`などが絡む場合は注意が必要だ。
- ONユニットとの相性:
`DEFINED`された変数を`ONCODE`や例外処理の対象にする場合、実行時にどの領域を指しているのかを常に意識せよ。メモリの重複箇所を片方で書き換えると、当然ながらもう片方の変数の値も変わる。これは副作用(Side Effect)の塊だ。
- 可搬性と保守性:
現代の保守現場では、「分かりやすさ」が「正しさ」に勝る。あまりにトリッキーなオーバーレイは、後任者に呪いをかけるようなものだ。どうしても必要な場合以外は、`UNION`属性の使用も検討し、意図を明確にコメントで残すのが流儀だ。
—
4. まとめ:賢く使い、安全に守る
`DEFINED`属性は、メインフレームの限られたリソースを極限まで使い切るために先人が編み出した「工夫」の結晶だ。
1. 目的を明確にする: なぜそのオーバーレイが必要なのか。
2. 型と長さを厳密に守る: 元の領域をはみ出すような定義は論外だ。
3. コメントを残す: 「なぜここでメモリを再定義したのか」という設計思想こそが、何年か後の改修時に自分を救うことになる。
PL/Iは、書き手次第でいかようにも化ける言語だ。`DEFINED`を正しく使いこなし、堅牢で効率的なシステムを構築してほしい。
もし、デバッグ中に「この変数の値が、なぜか勝手に変わっている……」と悩んだら、まず疑うべきはコードのどこかで行われている`DEFINED`による領域の衝突だ。現場からは以上だ。何かあればまた相談してくれ。
