皆さん、こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLの経験を積んで、「さあ、次はPL/Iのシステムだ!」と意気込んでいるそこのあなた。レガシー特有の独特な空気感や、画面の向こうで待ち構える謎のエラーコードに、ちょっぴり身構えていませんか?
「ポインタ? メモリの直接操作? うわ、難しそう……」
「COBOLにはなかった変な記号がいっぱいある……」
大丈夫です、安心してくださいね。怖がる必要はまったくありません。今回は、PL/Iプログラミングの醍醐味であり、同時に初心者の前に立ちはだかる最初の難関……「BASED変数とADDR/POINTER関数によるメモリ直接操作、そして恐怖のS0C4アベンド」について、一つずつ優しく解きほぐしていきましょう。
Javaの参照やCOBOLの`USAG IS POINTER`とは一味違う、PL/Iならではの「メモリを愛でる技術」を一緒にマスターしていきましょう!
—
1. JavaやCOBOLとは違う? PL/Iの「ポインタ」と「BASED変数」の正体
Javaなどのモダン言語では、メモリの番地(アドレス)を直接意識することはめったにありませんよね。GC(ガベージコレクション)が勝手に綺麗にしてくれますし、オブジェクトの参照も安全にカプセル化されています。
一方、COBOLでもポインタは扱えますが、どちらかといえば「リンケージ部(LINKAGE SECTION)での他プログラムとのデータ受渡し」や「テーブルの動的制御」が主役です。
しかし、PL/IのBASED変数とPOINTERは、もっとこう……C言語の「生(ロウ)のポインタ」に近い、ワイルドで強力な機能です。
イメージしてみましょう:マンションの「部屋番号」と「住人」
- POINTER(ポインタ): メモリ上の特定の場所を指し示す「部屋番号(メモリアドレス)」です。
- BASED変数: その部屋番号の場所に、「どんな家具を置き、どんな人間が住むか(データの構造)」を定義した図面です。
PL/Iのすごいところは、「同じメモリ空間(部屋)」であっても、貼る「看板(BASED変数)」を変えるだけで、数字に見えたり文字に見えたり、複雑な構造体に見えたりするところです。
1
/ ポインタ変数の宣言 /
DCL P_EMP_PTR PTR;
/ BASED変数の宣言(単なる図面であり、この時点ではメモリは確保されません) /
DCL 1 P_EMP_REC BASED(P_EMP_PTR),
5 EMP_ID CHAR(5),
5 EMP_NAME CHAR(20);
この「図面(BASED変数)」に実際の「部屋番号(ADDR関数で取得したアドレス)」をガチャンと合体させることで、その場所にあるデータを自由自在に読み書きできるようになるわけです。
—
2. 恐ろしい「S0C4アベンド」の正体を知ろう
さて、ここでメインフレームエンジニアなら誰もが一度は冷や汗をかいたことがある伝説のエラー、「S0C4アベンド(システム異常終了コード 0C4)」の登場です。
JCLの実行結果に「SYSTEM COMPLETION CODE=0C4」と出た瞬間、心臓がキュッと縮み上がりますよね。
このS0C4、原因の多くは「存在しないメモリの部屋に無理やり入ろうとした(保護違反)」か、今回焦点を当てる「境界アライメント違反」です。
アライメント(境界調整)ってなぁに?
メインフレーム(IBM Z)のハードウェアの仕様上、CPUがメモリからデータを読み書きするときには「お行儀の良さ」が求められます。
- 半ワード(2バイト)整数: 2の倍数のアドレスから始まってほしい!
- フルワード(4バイト)整数: 4の倍数のアドレス(下位2ビットが `00`)から始まってほしい!
- ダブルワード(8バイト)整数 / 浮動小数点数: 8の倍数のアドレス(下位3ビットが `000`)から始まってほしい!
もし、奇数番地など「中途半端なアドレス」から4バイトの数値を無理やり読み込もうとすると、CPUは「うわ、キリが悪いよ!」とパニックを起こします。これがアライメント違反であり、容赦なくS0C4アベンドを引き起こすトリガーとなるのです。
—
3. 実践!安全なBASED変数アクセスとアライメント回避のコツ
百聞は一見にしかず。実際に安全なコードの書き方を見てみましょう。
以下のサンプルは、取得したワークエリアの先頭アドレスをポインタで受け取り、 BASED変数経由で安全に数値や文字を処理する実用的なパターンです。
1
/ ================================================================= /
/ プログラム名: MEMSMPL /
/ 概要: BASED変数とADDR/POINTER連携による安全なデータ操作サンプル /
/ ================================================================= /
MEMSMPL: PROC OPTIONS(MAIN);
效 (日本語コメント: 変数の宣言を行います) /
DCL W_WORK_AREA CHAR(100) INIT((100) ‘0’X); / 100バイトの作業域 /
DCL V_PTR PTR; / 汎用ポインタ /
/ 4バイトの固定小数点数(フルワード)を定義するBASED変数 /
/ ALIGNED属性を明示することが、アライメント違反を防ぐ最大のコツです! /
DCL 1 S_DATA_MAP BASED(V_PTR),
3 F_COUNT FIXED BIN(31,0) ALIGNED,
3 F_NAME CHAR(12);
/ 1. 作業域の先頭アドレスをポインタにセット /
V_PTR = ADDR(W_WORK_AREA);
/ 2. アライメントの確認と安全な値の設定 /
/ 固定小数点数(FIXED BIN(31))を扱うため、アドレスが4の倍数かを意識します /
/ 通常、CHAR変数の先頭は奇数番地から始まることもあるため注意が必要です! /
/ ここでは安全のために、アドレスの境界を整える、あるいはアライメント付きで定義します /
F_COUNT = 12345; / カウンタに値を設定 /
F_NAME JAPAN’; / 名称を設定 /
/ 結果の確認(実際にはDISPLAYなどでログに出力) /
DISPLAY(‘COUNT = ‘ || TRIM(F_COUNT));
DISPLAY(‘NAME = ‘ || F_NAME);
END MEMSMPL;
ここがプロの知見:`ALIGNED` と `UNALIGNED` の魔法
PL/Iのデータ宣言では、デフォルトでデータ型に応じたアライメント(境界調整)が効きます(`ALIGNED`)。しかし、ストレージを1バイトたりとも無駄にしたくないがために、`UNALIGNED`(非境界調整)を指定してしまうことがあります。
ここで注意!
`UNALIGNED` を指定した構造体のメンバーに対して、ポインタ経由で直接 `FIXED BIN(31)` などの数値を高速に読み書きしようとすると、先ほどお話ししたS0C4アベンドや、ハードウェア内部での性能劣化(ハードウェア・アシストが効かないことによるスローダウン)が発生します。
- 数値をガリガリ計算するBASED変数:原則として `ALIGNED` を維持する!
- 通信電文やファイルレイアウトで1バイトのズレも許されないパディングなしのデータ:ポインタで直接数値をいじるのではなく、一度 `UNALIGNED` なエリアから `UNSPEC` や `SUBSTR` できれいな変数に代立替えてから演算する!
この一手間を惜しまないことが、夜間バッチをS0C4から守る最高の防衛策となります。
—
4. おわりに
いかがでしたでしょうか?
「BASED変数」「ADDR関数」「ポインタ」、そして「アライメント」。名前だけ聞くと難解な呪文のようですが、「メモリという部屋に、正しい寸法(アライメント)で家具(BASED変数)を配置する」というイメージを持てば、もう怖くありませんよね。
PL/Iは、ハードウェアの特性をダイレクトに引き出せる、非常にパワフルで美しい言語です。レガシーシステムの改修やマイグレーションの現場でこの知識に出会ったとき、この記事があなたの心強い羅針盤となれば幸いです。
それでは、次回のメインフレーム・ワンポイントレッスンでお会いしましょう!快適なPL/Iライフを!
