PL/Iの「参照渡し(BYADDR)」を恐れないで!メインフレームの深淵へようこそ
こんにちは。長年メインフレームの「深淵」を覗き込み、時には格闘してきたシステムアーキテクトです。
JavaやCOBOLからメインフレームの世界に足を踏み入れた皆さん、ようこそ。PL/I(Programming Language One)という名前を聞いて、「何だか古臭くて難解そうだな…」と身構えていませんか? 大丈夫です。PL/Iは、その柔軟すぎる仕様ゆえに「最強の言語」とも呼ばれますが、基本さえ押さえれば、これほど頼りになる相棒はいません。
今回は、実務のバッチ改修で最も「ハマりやすい」ポイントの一つ、`CALL`文における「参照渡し(BYADDR)」の裏側について、じっくり紐解いていきましょう。
—
「値」を渡すのではなく、「住所(アドレス)」を渡すという感覚
Javaでオブジェクトを扱うとき、私たちは「参照」という言葉を使いますよね。PL/Iのデフォルトの挙動も、まさにそれに近いものです。
PL/Iで`CALL`文を実行する際、特別な指定をしない限り、引数はBYADDR(参照渡し)として扱われます。これは、「変数の値をコピーして渡す」のではなく、「変数が格納されているメモリ上の『住所(アドレス)』をメモに書いて渡す」というイメージです。
なぜ「住所」を渡すのか?
理由はシンプルで、メインフレームという限られたリソースの中で、巨大なデータ構造(構造体など)を何度もコピーするのはメモリの無駄だからです。「住所」だけを渡せば、呼び出し先(CALLED)は、その場所を見に行って直接データを書き換えることができます。効率的でしょう?
—
実践:呼び出し先で値を書き換えるとどうなる?
百聞は一見にしかず。コードで見てみましょう。呼び出し元(MAIN)で定義した変数が、サブプログラム側でどう扱われるのかを確認します。
1
/ メインプログラム:呼び出し元 /
MAIN_PROG: PROCEDURE OPTIONS(MAIN);
DCL MY_VALUE FIXED BIN(31) INIT(100);
PUT SKIP LIST(‘呼び出し前:’, MY_VALUE);
/ サブプログラムを呼び出す /
CALL SUB_CALC(MY_VALUE);
/ ここで注目!MY_VALUEは100から200に書き換わっている /
PUT SKIP LIST(‘呼び出し後:’, MY_VALUE);
END MAIN_PROG;
/ サブプログラム:呼び出し先 /
SUB_CALC: PROCEDURE(P_VAL);
DCL P_VAL FIXED BIN(31); / BYADDR(参照渡し)として受け取る /
/ P_VALはメイン側の変数の「住所」を指しているため /
/ ここで行う変更は、メイン側の変数そのものに影響する /
P_VAL = P_VAL + 100;
END SUB_CALC;
このコードを実行すると、呼び出し後の `MY_VALUE` は確実に `200` になっています。
「えっ、勝手に書き換わったら困るよ!」という声が聞こえてきそうですね。そう、これがPL/Iの「諸刃の剣」です。参照渡しである以上、呼び出し先での代入は、呼び出し元の変数を直接書き換えてしまうのです。
—
「怖い」を防ぐためのPL/I流の知恵
もし、「呼び出し先で勝手に値をいじられたくない!」という場合はどうすればいいでしょうか。そんな時は、`VALUE`属性を使って「値渡し」にするのが鉄則です。
1
/ 値渡しにするための修正例 /
SUB_CALC: PROCEDURE(P_VAL);
/ VALUE属性をつけると、値のコピーが渡される(BYVALUE相当) /
DCL P_VAL FIXED BIN(31) VALUE;
P_VAL = P_VAL + 100; / この変更はローカルなコピーのみに反映される /
END SUB_CALC;
このように `VALUE` 属性を付けると、PL/Iコンパイラは内部的に「あ、これはコピーを渡せばいいんだな」と判断してくれます。呼び出し元の `MY_VALUE` は無傷で守られます。
—
アーキテクトからのアドバイス
実務の現場でトラブルが起きるのは、この「参照渡し」の特性を知らずに、「渡した先で勝手に値が変わってしまい、予期せぬバグが発生する」ケースです。
特に、歴史の長い基幹システムでは、サブプログラムの引数が何を期待しているのか(単なる入力値なのか、出力用変数の入れ物なのか)が明示されていないことがあります。
- デバッグのヒント: 呼び出し先のサブプログラムで、引数が意図せず書き換えられていないか?
- 設計のヒント: 変更してほしくない引数には、呼び出し側で `(MY_VALUE)` のように括弧で囲むことで、定数として(値渡しで)強制的に渡すテクニックもあります(※これは少し上級編ですが、覚えておくと便利です)。
PL/Iは古い言語ですが、メモリを直接扱うこの「強さ」が、基幹業務を何十年も支え続けている理由です。怖がることはありません。一つひとつ、メモリの地図を頭に描きながらコードを追ってみてください。
もし、どうしても挙動が怪しいコードに出会ったら、またいつでも聞きに来てくださいね。あなたのメインフレーム・ライフが、実りあるものになりますように!
