こんにちは!IBMメインフレームの世界へようこそ。
JavaやCOBOLといった他の言語の経験はあるけれど、「PL/I(ピーエルワン)」を初めて触ることになって、ちょっぴりドキドキしていませんか?
「なんだか古い言語らしいし、メモリを直接いじるとか怖そう……」
「ポインタって闻くだけで、なんだかバグの温床になりそうで不安……」
そんな風に思っていらっしゃる方も多いのではないでしょうか。でも、安心してくださいね。レガシーの世界であっても、仕組みさえ分かってしまえば、PL/Iは非常にパワフルで、かつプログラマの意図に忠実な優しい言語なんです。
今回は、PL/Iの数ある強力な機能の中でも、基幹システムの裏側でこっそり(そしてダイナミックに)データを支えている「POINTER属性」と「ADDRビルトイン関数」について、一緒に紐解いていきましょう!
—
そもそも「ポインタ」って、現実世界で例えると?
JavaやCOBOLしか触ったことがない方にとって、「メモリのアドレスを直接指す」なんて聞くと、なんだかSF映画のハッキングシーンのように感じられるかもしれません。
でも、身近な例で考えてみてください。
あなたは巨大なマンション(=メインフレームのメインメモリ)の管理人さんです。住人(=データ)に荷物を届けるとき、「〇〇様」という名前で探すのもいいですが、何万世帯もあるビルでは時間がかかりますよね。
そこで、「3階の102号室」という部屋番号そのもの(=メモリのアドレス)をメモに書いて渡されたらどうでしょう? 名前の確認すら不要で、一瞬でその部屋にたどり着けますよね。
この「部屋番号が書かれたメモ」こそが、PL/IにおけるPOINTER(ポインタ)なんです。そして、その住人が今「どの部屋にいるのか」をこっそり教えてくれる盗聴器…いや、親切な案内板がADDRビルトイン関数というわけです。
—
31ビットと64ビット:アドレスの世界が広がる話
メインフレームの歴史は長く、アドレスの幅(ビット数)も時代とともに進化してきました。
- 24ビット・アドレッシング: 昔の古い世界。16MBまでしかメモリが使えませんでした。
- 31ビット・アドレッシング: 現在の基幹システムでも主流としてバリバリ現役で動いている世界。約2GBまでの空間を扱えます。「あれ、32ビットじゃないの?」と思われるかもしれませんが、最上位ビットをフラグに使っていた歴史的経緯から31ビットと呼ばれています。
- 64ビット・アドレッシング: 近年の超大容量を必要とするビッグデータや、最新のz/OS環境で使われる広大な世界。
PL/Iでは、このアドレスを格納する変数(ポインタ変数)を非常にシンプルに宣言できます。
1
DCL P_DATA POINTER; / 31ビット/64ビットに対応するポインタ変数の宣言 /
コンパイルオプションや環境(AMODE 31 / AMODE 64)によって、このポインタが指し示す空間の広さが自動的に切り替わります。僕たちが書くコードの基本作法は変わらないので、そこは安心してくださいね。
—
実践!ADDR関数とポインタで変数をのぞき見してみよう
百聞は一見にしかず。実際にPL/Iでメモリを直接覗き見するコードを見てみましょう。
COBOLでいう `REDEFINES` や、C言語のポインタ操作に近い感覚ですが、PL/Iならではのスマートさがあります。
1
/ ======================================================= /
/ ポインタとADDR関数の基本サンプル /
/ ======================================================= /
TEST_PROG: PROC OPTIONS(MAIN);
/ 通常の文字変数と、それを指し示すポインタを宣言 /
DCL WK_MSG CHAR(20) INIT(‘HELLO MAINFRAME!’);
DCL P_MSG POINTER;
/ ポインタ経由でデータをマッピングするためのベース変数 /
DCL 1 DUMMY_AREA BASED(P_MSG01),
3 FST_10 CHAR(10),
3 LST_10 CHAR(10);
DCL P_MSG01 POINTER;
/ 1. ADDR関数を使って、WK_MSG変数のメモリ上の住所(アドレス)を取得する /
P_MSG01 = ADDR(WK_MSG);
/ 2. ポインタ経由で中身を表示してみる /
DISPLAY(‘元データ: ‘ || WK_MSG);
/ 3. BASED変数(ポインタの住所にお家を建てるイメージ)を通してアクセス /
DISPLAY(‘前半10バイト: ‘ || FST_10);
DISPLAY(‘後半10バイト: ‘ || LST_10);
END TEST_PROG;
コードの解説:ここで何が起きているの?
1. `ADDR(WK_MSG)`
これが今回の主役の一つ、ADDRビルトイン関数です。「`WK_MSG`って変数、今メモリのどこにいるの?」とシステムに聞き、その場所(アドレス)をポインタ変数 `P_MSG01` にスポッと格納します。
2. `BASED(P_MSG01)`
ここがPL/Iの真骨頂です。`BASED` 属性がついた構造体は、「単体では実体を持たない、空っぽの型枠」です。しかし、ポインタ変数(ここでは `P_MSG01`)と結びつけることで、「その住所にあるメモリを、この構造体の形(10文字+10文字)で無理やり解釈しなさい!」という強力な魔法を発動します。
Javaのオブジェクト参照のように安全にカプセル化されている世界から来ると、「えっ、そんなメモリの解釈を勝手に変えて大丈夫なの!?」とハラハラしちゃいますよね。
—
メモリ直接参照の危険性と、現場で生きる心構え
さて、ここからが少しシリアスな、でも現場では一番大切な「危険性」のお話です。
ポインタや `ADDR` 関数、そして `BASED` 変数を使うと、COBOLの通常のデータ部では絶対にできないような「メモリの強制的ハック(オーバーレイ)」ができてしまいます。
例えば、数値が入っているエリアに無理やり文字型を重ね合わせたり、ポインタが変なところ(すでに解放されたメモリや、存在しないアドレス)を指したまま書き込みを行ったりすると……。
メインフレームの世界ではお馴染みの、あの恐ろしいエラーが発生します。
そう、S0C4(クセロシー・ゼロ・シー・ヨン)アベンドです!
> S0C4アベンドとは?
> プログラムが「アクセスしてはいけないメモリ領域」に触れてしまったときに、OS(z/OS)が激怒して強制終了させるエラーです。「おい、そこは勝手に入っていい部屋じゃないぞ!」というOSからのレッドカードですね。
安全に使いこなすための3つの教訓
1. ポインタの初期化を忘れない
宣言しただけのポインタは、ゴミ(不定値)が入っています。そのまま `BASED` 変数を使うと、いきなりS0C4の餌食になります。使わないときは `NULL()` で初期化する癖をつけましょう。
2. ストレージの寿命(スコープ)を意識する
自動変数(サブルーチンを抜けたら消える変数)の住所をポインタに保持したまま、そのサブルーチンを抜けてしまうと、いわゆる「野良ポインタ(ダングリング・ポインタ)」になり、次に何が起きるか予測がつきません。
3. 本当にそのダイレクト参照が必要か自問する
現代のPL/Iマイグレーションや保守において、むやみやたらにポインタをこねくり回すコードは、後任のプログラマ(あるいは未来のあなた)を泣かせる原因になります。「どうしても構造体を動的に扱いたい」「超高速なバッチで領域を再利用したい」といった正当な理由がある場合にのみ、慎重に使いましょう。
—
おわりに
いかがでしたでしょうか?
PL/Iの `POINTER` 属性と `ADDR` 関数は、まるで諸刃の剣のように強力ですが、仕組みを理解して正しく使えば、メインフレームの限られたリソースを極限まで引き出す最高のアシスタントになります。
「メモリの住所を指すメモ(ポインタ)」と「住所を教えてくれる案内人(ADDR)」というイメージさえ持っていれば、レガシーなコードを読まなければならない時も、もう怖くありません。
もし実務のマイグレーションやバッチ改修で分からないコードに出会ったら、ぜひ今回の話を思い出してみてくださいね。あなたのメインフレームライフが、少しでも楽しく、安心なものになりますように!
