【入門編】PROCEDURE引数受け渡しにおけるBYADDRとBYVALUEのメモリレイアウト差異 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは。メインフレームの世界へようこそ。

あなたがこれまでJavaやCOBOLで培ってきた知識は、ここでも間違いなく武器になります。ただ、PL/Iという言語は少しだけ「職人気質」なところがあって、メモリという広大な土地の管理を、プログラマにかなり委ねてくるんです。

今日は、メインフレームの現場で新人が最初に直面する「なぜか動かない」「突然のアベンド(異常終了)」の代表格、引数の受け渡し(BYADDRとBYVALUE)について、お話ししましょう。

1. そもそも「BYADDR」と「BYVALUE」って何が違うの?

PL/Iでサブプログラムを呼び出すとき、引数をどう渡すかを指定できます。これがデフォルトのままだと、思わぬところで牙をむくことがあります。

〇 BYADDR(アドレス渡し):荷札だけを渡す

「そのデータがある場所(住所)」だけを教えてあげる渡し方です。

  • イメージ: 「このメモにある住所の家に、私の荷物があるから勝手に見てきて」と、住所を書いた紙だけ渡す。
  • メリット: データがどんなに大きくても、紙一枚渡すだけなので速い。呼び出し先で値を書き換えると、元のデータも変わる。

〇 BYVALUE(値渡し):荷物そのものを渡す

データの中身をコピーして、相手に丸ごと渡す渡し方です。

  • イメージ: 「この箱の中身を全部あげる!」と、現物を手渡す。
  • メリット: 安全。渡した先で何があろうと、元のデータには影響しません。

2. なぜ「S0C4(保護例外)」で落ちるのか?

ここが一番の落とし穴です。「呼び出し側」と「呼び出し先」で、この約束(ADDRなのかVALUEなのか)が食い違っていると、プログラムはパニックを起こします。

例えば、呼び出し先が「あ、この引数は値(BYVALUE)で来るんだな」と思って待ち構えているのに、呼び出し側が「住所(BYADDR)」を渡してしまったらどうなるでしょう?

呼び出し先は、受け取った「住所」を、あろうことか「値そのもの」だと勘違いして読み取ろうとします。メモリ上の「変な場所」をデータとしてアクセスしようとするわけです。これがSystem Completion Code 0C4(保護例外)の正体です。

3. 実践:コードで見てみよう

実際に、よくある失敗例を見てみましょう。

1
/ — 呼び出し元 (CALLER) — /
TEST_PROC: PROCEDURE OPTIONS(MAIN);

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

/ ここではデフォルトで BYADDR として渡される /
CALL SUB_PROC(VAL_DATA);

END TEST_PROC;

/ — 呼び出し先 (CALLEE) — /
SUB_PROC: PROCEDURE(IN_VAL) OPTIONS(REENTRANT);

/ 間違い! BYVALUE で受け取ると宣言してしまった /
DCL IN_VAL FIXED BIN(31) BYVALUE;

/

  • ここで IN_VAL を使おうとすると…
  • 実際には「100」という値ではなく、VAL_DATAのアドレス値(例: X’000A1234′)
  • を数値として処理しようとするため、メモリ保護違反でS0C4発生!

/
PUT SKIP LIST(‘値は:’, IN_VAL);

END SUB_PROC;

どう直せばいい?

呼び出し先(SUB_PROC)の宣言を合わせるのが基本です。

1
/ 正しい宣言例 /
SUB_PROC: PROCEDURE(IN_VAL) OPTIONS(REENTRANT);

/ 呼び出し元がアドレスを渡してくるなら、こちらも BYADDR を明示する /
DCL IN_VAL FIXED BIN(31) BYADDR;

/ これで正しく「100」が参照できる /
PUT SKIP LIST(‘正しく値を受け取りました:’, IN_VAL);

END SUB_PROC;

4. アーキテクトからのアドバイス:安全のために

現場でのトラブルを避けるために、私はいつも以下のことを意識しています。

1. 「明示的」に書く癖をつける
PL/Iはデフォルトの挙動(コンパイラオプションに依存する仕様)が多いため、`BYADDR` や `BYVALUE` は省略せずに書くのが、後のメンテナンス担当者への最大の優しさです。
2. インターフェース定義を共通化する
メインフレームの古いシステムでは、プロシージャの宣言をコピーブック(`%INCLUDE`)で共有していることが多いはずです。引数の属性を共有定義に閉じ込めておけば、呼び出し側と呼び出し先の不一致は未然に防げます。
3. S0C4を恐れない
もし落ちてしまったら、ダンプリストを見てください。レジスタに「変なアドレス」が入っていませんか? それが、呼び出し側で渡した変数のアドレスと一致していたら……「あ、渡し方が逆だったんだな」とすぐに特定できます。

PL/Iは、メモリという「基盤」に直接触れられる、非常に強力で美しい言語です。最初は戸惑うかもしれませんが、一つひとつの宣言がメモリ上でどう動いているかを想像できるようになれば、あなたはもう一人前のメインフレーム・エンジニアです。

もしまた分からないことがあれば、いつでも聞きに来てくださいね。一緒にコードを紐解いていきましょう。

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