【入門編】ADDRとENTRYビルトイン関数の違い – PL/Iの基本構文とデータ制御実践ガイド

PL/Iの「アドレス」と「エントリポイント」、どう違うの? 初心者さん、メインフレームの世界へようこそ!

こんにちは!メインフレームのベテランアーキテクトです。今日は、PL/Iの世界に足を踏み入れたばかりの皆さん、特にJavaやCOBOLの経験はあるけれど、PL/Iは初めて、という方のために、ちょっぴり不思議で、でもとっても便利な「ADDR」と「ENTRY」という二つのビルトイン関数について、じっくりお話ししたいと思います。

メインフレームの世界って、聞くだけでちょっと敷居が高いように感じますよね。でも大丈夫!PL/Iの基本構造から、データがどうやってメモリに格納されるのか、そして今日の本題であるADDRとENTRYの違いまで、一つずつ丁寧に紐解いていきましょう。まるで、初めての街を地図を片手に歩くように、私がしっかりナビゲートしますから、安心してくださいね。

PL/Iプログラムの「骨格」を知ろう:PACKAGE、PROCEDURE、OPTIONS(MAIN)

まず、PL/Iプログラムがどんな構造になっているのか、基本的な「骨格」を見ていきましょう。他の言語でいうところの「クラス」や「メソッド」、「メイン処理」みたいなものだと考えてください。

  • PACKAGE: これは、PL/Iプログラムを構成する「部品」をまとめるための箱のようなものです。関連する手続き(PROCEDURE)や変数などをまとめて管理するのに役立ちます。Javaでいうと、パッケージ(package)に似た感覚ですね。
  • PROCEDURE: これは、一連の処理をまとめた「手続き」や「関数」のようなものです。特定のタスクを実行するために書かれます。PL/Iでは、このPROCEDUREがプログラムの実行単位となることが多いんですよ。
  • OPTIONS(MAIN): これは、そのPROCEDUREがプログラムの「開始点」であることを示す特別な指定です。プログラムを実行したときに、最初に動き出す場所を指定するんですね。Javaでいう `public static void main(String[] args)` のような役割を担います。

例えば、こんな風に書かれます。

MYPROG: PROCEDURE OPTIONS(MAIN);
/ ここにプログラムの処理を書きます /
DECLARE MY_VARIABLE FIXED BINARY(31);

MY_VARIABLE = 123;

/ 他の手続きを呼び出すこともできます /
CALL ANOTHER_ROUTINE;

END MYPROG;

ANOTHER_ROUTINE: PROCEDURE;
/ 別の処理 /
PUT LIST(‘This is another routine.’);
END ANOTHER_ROUTINE;

この例では、`MYPROG` という名前のPROCEDUREが `OPTIONS(MAIN)` を持っているので、これがプログラムの開始点になります。`ANOTHER_ROUTINE` は、`MYPROG` から `CALL` で呼び出される別の手続きですね。

メモリの「住所」を知りたい! ADDR関数

さて、ここからが今日のメインイベントです。まず、ADDR関数についてお話ししましょう。

皆さんは、プログラムの中で変数を使いますよね?例えば、先ほどの例にあった `MY_VARIABLE` のようなものです。この変数は、コンピューターのメモリ(一時的な記憶場所)の中に格納されています。ADDR関数は、この「メモリの中のどこに格納されているか」という、まさに変数の「住所」を取得するための関数なんです。

イメージとしては、皆さんがAmazonなどで何かを買ったときに、商品が保管されている倉庫の「棚番」や「ロケーション」を知りたい、という感覚に似ています。ADDR関数は、その変数がメモリのどの番地に置かれているか、その番地情報を返してくれるんですね。

PL/Iでは、変数を宣言する際に、その変数がメモリ上でどのように扱われるかを指定する「属性」を付けます。例えば、`FIXED BINARY(31)` というのは、「31ビットの固定小数点2進数」という意味で、メモリ上でどれくらいの領域を占めるかが決まっています。ADDR関数は、この宣言された変数に割り当てられたメモリ上のアドレスを返してくれるんです。

ADDR関数の実用例

では、実際にADDR関数を使ってみましょう。

SAMPLE_PROG: PROCEDURE OPTIONS(MAIN);

DECLARE MY_STRING CHARACTER(20) VARYING; / 長さ可変の文字列 /
DECLARE MY_PTR POINTER; / ポインタ変数 (アドレスを格納する変数) /

MY_STRING = ‘Hello, ADDR!’;

/ MY_STRING 変数のメモリ上のアドレスを取得し、MY_PTR に格納 /
MY_PTR = ADDR(MY_STRING);

/ 取得したアドレスを表示してみましょう /
PUT LIST(‘MY_STRING のアドレスは: ‘, MY_PTR);
PUT SKIP; / 改行 /

/ ポインタを使って、そのアドレスにある値にアクセスすることも可能 /
/ ただし、この例では直接MY_STRINGにアクセスする方が一般的です /
/ PUT LIST(‘アドレス ‘, MY_PTR, ‘ の値は: ‘, MY_PTR->MY_STRING); /
/ 上記のようなポインタ経由でのアクセスは、より複雑なシナリオで使われます /

END SAMPLE_PROG;

このコードでは、`MY_STRING` という文字変数と、`MY_PTR` というポインタ変数(アドレスを格納するための変数)を宣言しています。そして、`MY_PTR = ADDR(MY_STRING);` という部分で、`MY_STRING` がメモリ上のどこにあるか、そのアドレスを `MY_PTR` に格納しています。

ADDR関数は、変数の「物理的な」場所、つまりメモリ上のアドレスを知りたいときに使われる、と覚えておくと良いでしょう。

プロシージャの「入り口」を知りたい! ENTRY関数

さて、次にENTRY関数です。ADDR関数が「データ変数」のメモリ上のアドレスを取得するのに対し、ENTRY関数は「プロシージャ(手続き)」の「エントリポイント(開始点)」のアドレスを取得するための関数です。

これはどういうことかというと、プログラムの中にはたくさんの手続き(PROCEDURE)がありますよね。ENTRY関数は、その手続きが「どこから始まるか」という、その手続きへの「入り口」の場所を教えてくれるんです。

例えるなら、街の地図で、あるお店(手続き)の「入口のドア」がどこにあるかを知りたい、という感覚です。ADDR関数が「商品の棚番」なら、ENTRY関数は「お店のドアの場所」というイメージですね。

ENTRY関数が返す値は、ある種の「手続きへのポインタ」のようなものだと考えると分かりやすいかもしれません。この値を使うことで、プログラムの実行中に、別の手続きを指し示すことができるようになるんです。

ENTRY関数の実用例

ENTRY関数も、実際にコードで見てみましょう。

MAIN_PROG: PROCEDURE OPTIONS(MAIN);

DECLARE ANOTHER_ROUTINE_ADDR ENTRY; / 手続きのアドレスを格納するためのENTRY型変数 /
DECLARE ROUTINE_PTR POINTER; / ポインタ変数 /

/ ANOTHER_ROUTINE という手続きのエントリポイントのアドレスを取得 /
ANOTHER_ROUTINE_ADDR = ENTRY(ANOTHER_ROUTINE);

/ 取得したアドレスをポインタ変数に代入 /
ROUTINE_PTR = ANOTHER_ROUTINE_ADDR;

PUT LIST(‘ANOTHER_ROUTINE のエントリポイントアドレス: ‘, ROUTINE_PTR);
PUT SKIP;

/ (注:PL/IではENTRY関数で取得したアドレスを直接CALLすることは一般的ではありません。 /
/ 通常は、ENTRY型変数に代入し、そのENTRY型変数を介して呼び出します。 /
/ 例: CALL ANOTHER_ROUTINE_ADDR; のようにENTRY型変数を直接呼び出します。) /

/ ENTRY型変数を通じて手続きを呼び出す /
CALL ANOTHER_ROUTINE_ADDR;

END MAIN_PROG;

ANOTHER_ROUTINE: PROCEDURE;
PUT LIST(‘これは ANOTHER_ROUTINE の処理です。’);
END ANOTHER_ROUTINE;

この例では、`ANOTHER_ROUTINE_ADDR` というENTRY型の変数を宣言しています。そして、`ANOTHER_ROUTINE_ADDR = ENTRY(ANOTHER_ROUTINE);` の部分で、`ANOTHER_ROUTINE` という手続きの「開始点」のアドレスを取得して、このENTRY型変数に格納しています。

PL/Iでは、ENTRY型変数に手続きのアドレスを格納しておき、そのENTRY型変数を `CALL` することで、動的に手続きを呼び出すことができるんです。これは、プログラムの柔軟性を高める上で非常に強力な機能ですよ。

ADDRとENTRY、ここが違う!

さて、ここまでADDRとENTRYについてお話ししてきましたが、一番の違いはどこにあるか、もうお分かりですよね?

  • ADDR関数: データ変数(文字変数、数値変数、構造体など)のメモリ上のアドレスを取得したいときに使います。
  • ENTRY関数: 手続き(PROCEDURE)エントリポイント(開始点)のアドレスを取得したいときに使います。

例えるなら、ADDR関数は「データベースのレコードの場所」を知りたいときに使い、ENTRY関数は「特定の機能を提供するAPIの入り口」を知りたいときに使う、というイメージでしょうか。

どちらも「アドレス」を取得するという点では似ていますが、何のアドレスを取得したいのか、という目的が全く異なるんです。

なぜ、こんな機能が必要なの? ~レガシーシステムのリアル~

「でも、わざわざメモリのアドレスや手続きのエントリポイントなんて知る必要あるの?」って思われたかもしれませんね。

確かに、最近の言語では、メモリ管理は自動で行われ、手続きの呼び出しも直接的で分かりやすいことが多いです。しかし、メインフレームのPL/Iの世界では、これらの機能が非常に重要な役割を果たすことがあります。

  • ポインタを使った高度なデータ構造: 複雑なデータ構造を構築したり、メモリを効率的に管理したりするために、ポインタとADDR関数は欠かせません。
  • 動的な手続き呼び出し: プログラムの実行中に、どの手続きを呼び出すかを決定したい場合、ENTRY関数が活躍します。例えば、設定ファイルの内容によって、実行する処理を切り替えたい、といったケースです。
  • 外部ルーチンとの連携: 外部のライブラリや、別のプログラムで定義された手続きを呼び出す際にも、ENTRY関数が使われることがあります。
  • パフォーマンスチューニング: プログラムのボトルネックを特定し、メモリ使用量や処理速度を最適化するために、変数のアドレスを意識することがあります。

これらの機能は、レガシーシステムを改修したり、新しいシステムに移行(マイグレーション)したりする際に、コードを深く理解するために避けては通れない道なんです。最初は難しく感じるかもしれませんが、これらのビルトイン関数を理解することで、PL/Iプログラムの「深層」に触れることができ、よりパワフルなコードを書けるようになりますよ。

まとめ

今日は、PL/IのADDR関数とENTRY関数の違いについて、じっくりとお話ししました。

  • ADDR: 変数の「メモリ上の住所」
  • ENTRY: 手続きの「入り口の場所」

この二つの違いをしっかりと理解することで、PL/Iプログラムの構造やデータの流れが、よりクリアに見えてくるはずです。

メインフレームの世界は、一見すると古くて複雑に見えるかもしれませんが、その中には長年培われてきた、確かな技術と知恵が詰まっています。今日お話ししたADDRやENTRYのような機能も、その一部です。

これからも、皆さんがPL/Iの世界を楽しく、そして力強く歩んでいけるように、私がそっと寄り添って、様々な知見をお伝えしていきたいと思います。もし、今日の話で「なるほど!」と思っていただけたら嬉しいです。

それでは、また次回!

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