【実務・中級編】CALL文における引数のBYADDR(参照渡し)の内部挙動 – PL/Iの基本構文とデータ制御実践ガイド

現場のPL/Iエンジニアに捧ぐ:CALL文の「参照渡し」という見えない罠と正しい付き合い方

メインフレームの世界で数十年、数多のバッチシステムが火を噴く現場を渡り歩いてきたが、PL/Iにおいて「CALL文の引数」ほど、ベテランと若手の差がはっきり出るポイントはない。

「なんとなく動いているから」で済ませていると、ある日突然、デバッグ不可能なデータの破壊や、意図しないメモリの書き換えという悪夢に遭遇する。今日は、PL/Iのデフォルトである`BYADDR`(参照渡し)の内部挙動について、現場の勘所を交えて解説する。

1. なぜ「参照渡し」がデフォルトなのか

PL/Iの歴史は、科学技術計算から事務処理までをカバーする汎用性の追求の歴史だ。当時の限られたメモリリソースの中で、巨大な構造体や配列をいちいちコピー(値渡し)していたら、メインフレームといえど悲鳴を上げる。

そこで選ばれたのが、引数の「アドレス(ポインタ)」だけを渡す`BYADDR`だ。呼び出し先は、渡されたアドレスを頼りに、呼び出し元のメモリ領域を直接叩く。この「効率の良さ」こそがPL/Iの真骨頂だが、同時に「副作用」という諸刃の剣を我々に突きつける。

2. 実践コード:呼び出し元の領域が書き換わる瞬間

百聞は一見に如かず。以下のコードを見てほしい。特に`SUB_CALC`が呼び出し元の`WK_VAL`をどう扱っているかに注目してほしい。

1
/ ————————————————————- /
/ MAINプログラム側からの呼び出し例 /
/ ————————————————————- /
MAIN_PROC: PROC OPTIONS(MAIN);

DCL WK_VAL FIXED BIN(31) INIT(100);

PUT SKIP LIST(‘呼び出し前:’, WK_VAL);

/ 参照渡しによりWK_VALのアドレスが渡される /
CALL SUB_CALC(WK_VAL);

/ 呼び出し先で変更された値が戻ってくる /
PUT SKIP LIST(‘呼び出し後:’, WK_VAL);

MAIN_PROC_END: END MAIN_PROC;

/ ————————————————————- /
/ 呼び出し先サブプログラム /
/ ————————————————————- /
SUB_CALC: PROC(P_VAL);

DCL P_VAL FIXED BIN(31);

/ ここでP_VALを更新すると、MAIN側のWK_VALも連動して変わる /
P_VAL = P_VAL 2;

RETURN;
END SUB_CALC;

このプログラムを実行すると、出力は当然「200」になる。`SUB_CALC`内の`P_VAL`は、`WK_VAL`の「別名(エイリアス)」に過ぎないからだ。

3. 現場で遭遇する「事故」と回避策

大規模改修で一番怖いのは、「呼び出し元は値が変わることを想定していないのに、呼び出し先が安易に引数を書き換えてしまう」ケースだ。

特にVSAMファイルから読み込んだデータをそのままサブプログラムに渡し、そこで「計算用のワーク領域」として引数を再利用してしまうと、メインルーチンに戻ったときには、肝心のデータが破壊されている……なんていう悲劇が起きる。

対策:BYVALUEとBYADDRの明示的利用

もし、サブプログラム側で値を書き換えても呼び出し元に影響を与えたくない場合は、`BYVALUE`を使用する。ただし、これには注意が必要だ。

1
/ 呼び出し先で明示的に値渡しを指定する /
SUB_SAFE: PROC(P_VAL_IN) BYADDR; / デフォルトはBYADDR /
DCL P_VAL_IN FIXED BIN(31) BYVALUE;

/ これならP_VAL_INを書き換えても、呼び出し元には影響しない /
P_VAL_IN = 999;
END SUB_SAFE;

※注:`BYVALUE`は、引数のサイズがポインタサイズ(4バイトまたは8バイト)に収まる場合に有効だ。巨大な構造体には使えないという制限がある。

4. デバッグのコツ:ONユニットとの組み合わせ

もし「どこで値が壊れているかわからない!」という状況に陥ったら、`ON ERROR`や`ON STORAGE`を活用しつつ、ダンプを確認する前に、まずは引数の受け渡しインターフェースを確認してほしい。

特に`CHAR`型変数の場合、`LENGTH`属性の不一致が原因で、呼び出し先が呼び出し元のメモリをはみ出して書き換える(バッファオーバーラン)ケースも多い。

  • チェックリスト:
  • 呼び出し元と呼び出し先の`DCL`属性(`FIXED BIN`の精度、`CHAR`の長さ)は完全一致しているか?
  • `OPTIONS(BYADDR)`を明示的に書くことで、仕様の意図を可視化できているか?
  • `BUILTIN`関数の`ADDR()`を使って、期待したアドレスが渡っているか確認したか?

最後に:メインフレームの流儀

PL/Iという言語は、コンピュータの物理的な挙動を隠蔽せず、かといって低級言語のように煩雑でもない。絶妙な抽象度を持っている。

「参照渡し」を理解するということは、メインフレームのメモリマップを頭の中に描くということだ。コードを書くとき、常に「今、自分はどこを触っているのか」を意識する。この感覚があれば、どんな難解なレガシー改修も怖くない。

次回のブログでは、VSAMアクセス時の`ONCODE`ハンドリングについて、もっと泥臭い話をしようと思う。現場のエンジニア諸君、今日もバッチの定時終了を祈っている。

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