【入門編】ON SUBSCRIPTRANGEによる配列添字範囲外アクセスの検出 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!IBMメインフレームの世界へようこそ。
JavaやCOBOLといった他のプログラミング言語の経験がおありの方なら、基幹システムの心臓部であるPL/I(ピーエルアイ)のコードを初めて目にしたとき、「なんだか独特な書き方だな…」と少し身構えてしまうかもしれませんね。

でも、大丈夫です。怖がる必要は全くありません。
今回は、PL/Iにおける「配列の添字(そえじ)の範囲外アクセス」という、Javaでいうところの `ArrayIndexOutOfBoundsException` に相当するトラブルを華麗に検知・解決するテクニックについて、そっと寄り添いながら優しく紐解いていきますね。

1. 他言語とはちょっと違う?PL/Iの変数の世界

JavaやCOBOLを触ってきた方にとって、配列の扱いは基本中の基本ですよね。例えばJavaなら `int[] scores = new int[10];` と宣言すれば、使えるインデックスは `0` から `9` までと決まっています。もし `scores[10]` なんてアクセスしようものなら、即座に例外が飛んできちんと教えてくれます。

では、PL/Iではどうでしょうか? PL/Iでももちろん配列は使えます。こんな風に書きます。

1
DCL SCORES(10) FIXED BIN(31); / 1から10までの要素を持つ配列 /

ここで、JavaやCOBOL経験者がまず驚くポイントがあります。
実はPL/Iの配列は、デフォルトでは 「1」から始まります(`0` ではありません!)。そして、もしあなたがうっかり `SCORES(11)` とか `SCORES(0)` なんて指定してしまったらどうなると思います?

他のモダンな言語のように親切に止まってくれればいいのですが、レガシーな世界では、コンパイラオプションの指定次第ではエラーを出さずに、メモリの隣の領域を勝手に書き換えてしまう(いわゆるメモリ破壊)という、背筋が凍るような挙動をすることがあります。

「えっ、じゃあバグを見つけるのがすごく難しそう…」と思いましたよね?
ご安心ください。それを強力にサポートしてくれるのが、今回主役の `SUBSCRIPTRANGE` という仕組みなんです。

2. コンパイラオプションとON条件で「境界チェック」を目覚めさせる

PL/Iで配列の範囲外アクセスを検知するには、大きく分けて「コンパイル時のおまじない」と「実行時の見張り番」の2つが必要です。

① コンパイラオプション:`STGOWND` や `SUBSCRIPTRANGE`

まず、コンパイラに対して「おい、配列の境界チェックのコードをちゃんと生成してくれよ!」と指示を出します。IBMのEnterprise PL/Iコンパイラでは、コンパイルオプションに `SUBR`(`SUBSCRIPTRANGE` の略)を指定します。

これをつけておくと、コンパイラは配列が参照されるたびに「今、指定された添字は定義された範囲内か?」をチェックするコードをこっそり埋め込んでくれます。

② 実行時の見張り番:`ON SUBSCRIPTRANGE`

チェックした結果、範囲外(オーバー)であることが判明したらどうなるでしょうか?
ここでプログラムが即死するのではなく、PL/Iには 「ONユニット」 という非常にユニークで強力な例外処理の仕組みが用意されています。

実際のコードの流れを見てみましょう。

1
/ ———————————————— /
/ 配列の添字範囲外チェックのサンプルプログラム /
/ ———————————————— /
TEST_PROG: PROC OPTIONS(MAIN);

/ 1から5までの配列を宣言 /
DCL WORK_TBL(5) FIXED BIN(31) INIT((5)0);
DCL I FIXED BIN(31);

/ ★ ここがポイント!範囲外アクセスを検知した時の動きを定義します /
ON SUBSCRIPTRANGE
BEGIN;
PUT SKIP LIST(‘【警告】配列の添字が範囲外を指しました!’);
/ 必要に応じてここで異常終了コードを設定したりできます /
SIGNAL ERROR; / 強制的にエラーを発生させる場合 /
END;

/ わざと範囲外(6番目)にアクセスしてみるテスト /
DO I = 1 TO 6;
WORK_TBL(I) = I 10; / I = 6 のときにトリガーが引かれます /
PUT SKIP LIST(‘WORK_TBL(‘, I, ‘) = ‘, WORK_TBL(I));
END;

PUT SKIP LIST(‘正常終了しました。’);

END TEST_PROG;

このコードを実行すると、ループの回数が配列の定義(`5`)を超えて `6` になった瞬間、`ON SUBSCRIPTRANGE` のブロックがフックされ、コンソールに警告メッセージを出して安全に止まる(あるいは独自のリカバリを行う)ことができます。

3. 実務の現場におけるデバッグ効率化のコツ

基幹システムのマイグレーションや、何百万ステップもある巨大なバッチプログラムの保守をしていると、原因不明のデータ破損や、理由の分からない異常終了(ABEND: S0C4など)に頭を悩ませることがよくあります。

「突然のメモリアクセス例外(S0C4など)」に直面したとき、それが配列の添字オーバーによるものなのか、ポインタ(ロケーター)の不正なのかを切り分けるのは至難の業です。

そんなとき、開発環境やテスト環境だけでも、この `SUBSCRIPTRANGE` を有効にした状態でテストランを行ってみてください。
「どこで、どの配列の何番目にアクセスして怒られたのか」が明確なメッセージとして浮かび上がってくるため、デバッグ効率が劇的に跳ね上がります。

ただし、一つだけ実務上のアドバイスを。
この境界チェック(監視コード)は、プログラムの実行時に毎回「範囲内かどうか」の比較演算を行うため、本番稼働するパフォーマンス最優先の巨大バッチでは、あえてこのオプションを外す(あるいは無効にする) ことがよくあります。
そのため、「開発・単体テスト段階では必ずONにしてバグを出し切り、本番では必要に応じて精査する」というメリハリ付けが、シニアなメインフレームエンジニアのスマートな立ち回りと言えます。

おわりに

レガシーなPL/Iと聞くと、難解で冷たいルールばかりあるように感じてしまうかもしれませんが、一つひとつの構文やオプションの意図を紐解いていくと、当時のプログラマたちが「いかに安全に、確実に動くシステムを作るか」を考えて作り上げた、非常に理にかなった設計であることが見えてきます。

「配列の範囲外アクセスが怖い」と思ったら、まずは `SUBSCRIPTRANGE` を味方につけてみてください。
あなたのメインフレームライフが、少しでも安心して楽しくなることを応援しています!

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