こんにちは。メインフレームの世界へようこそ。
COBOLの「REDEFINES」という言葉に馴染みがある方なら、PL/Iの「DEFINED」属性もすぐに仲良くなれます。でも、PL/IはCOBOLよりも少しだけ「メモリの深淵」に踏み込むような、自由度の高い言語です。
今日は、一つのメモリ領域を複数の顔で使い分けるための「DEFINED」と、そこに潜む「アライメント(位置合わせ)」という少し意地悪なクセについて、紐解いていきましょう。大丈夫、一つずつ見ていけば怖くありませんよ。
—
そもそも「DEFINED」って何?
簡単に言えば、「ある変数のメモリ領域を、別の名前(あるいは別の型)で覗き見る」ための仕組みです。
COBOLで言うところの `05 A PIC X(4). 05 B REDEFINES A PIC 9(4).` と同じようなものですが、PL/Iの `DEFINED` はもっと柔軟で、時には少しだけ危なっかしいほどに自由です。
/i
DCL BUFFER CHAR(8);
/ BUFFERという8バイトの領域を、別の名前で定義します /
DCL NEW_DATA CHAR(8) DEFINED(BUFFER);
こう書くと、`BUFFER` を書き換えれば `NEW_DATA` も変わりますし、その逆も然り。まるで一つの部屋に二つの入り口を作るようなイメージですね。
—
構造体で遊ぶ:型を変えて「覗き見る」
実務で一番多いのは、固定長テキストデータの中から「特定の4バイトだけを数値として扱いたい」といったケースです。
/i
DCL 1 RAW_DATA,
3 HEADER CHAR(4),
3 PAYLOAD CHAR(4);
/ RAW_DATAのPAYLOAD部分を、固定小数点数として定義し直す /
DCL PAYLOAD_NUM FIXED BIN(31) DEFINED(RAW_DATA.PAYLOAD);
/ こうすると、PAYLOADに文字で入れた数値を数値計算に使えるようになります /
ここで注意したいのが、「データ型を跨ぐときにはメモリの形を意識する」ということです。
—
注意!「アライメント(位置合わせ)」の罠
ここからが少しだけプロの話です。PL/Iには `ALIGNED` と `UNALIGNED` という属性があります。
メインフレームのCPUは、変数のデータ型に応じて「メモリのキリの良い場所(境界)」に置きたがります。例えば、4バイトの整数(FIXED BIN)は4の倍数の番地から始まるのが大好きなんです。
もし、あなたが `UNALIGNED` な構造体の中に強引に `ALIGNED` な変数を `DEFINED` して重ねようとすると、コンパイラは「あれ? 位置がズレちゃうよ!」と戸惑うか、あるいはパフォーマンスを犠牲にして強引にアクセスすることになります。
失敗しないための指針
- なるべく型を合わせる: `DEFINED` する際は、可能な限り元の型とサイズを意識しましょう。
- `UNALIGNED` を明示する: 特にファイルからの読み込みバッファなどは、`UNALIGNED` をつけておくと、「変な隙間(パディング)」を詰め込んでくれるので、予期せぬ位置ズレを防げます。
/i
DCL 1 MY_BUFFER UNALIGNED,
3 PART_A CHAR(2),
3 PART_B FIXED BIN(15) UNALIGNED;
/ UNALIGNEDをつけることで、メモリを隙間なく詰め込むのがコツです /
—
なぜこの仕組みが必要なのか
「今のJavaやPythonならそんなことしなくていいのに」と思うかもしれませんね。でも、基幹システムの現場では、数十年前に作られた「バイナリデータ」を読み込まなければならない場面が多々あります。
ある時は「文字」として読み取り、またある時は「計算用の数値」として解釈する。この `DEFINED` という魔法は、古いフォーマットを現代のロジックに適合させるための、いわば「翻訳機」のような役割を果たしているんです。
—
最後に:怖がらずに、まず書いてみよう
PL/Iの `DEFINED` は、メモリレイアウトを深く理解するための最高の教材です。最初は「アライメントって何?」と悩むかもしれませんが、動かしてみれば「あ、ここでズレるのか!」という発見が必ずあります。
もしエラーや警告が出ても、それはコンパイラが「あなたがやりたいことは、メモリの配置的にちょっと無理があるかもしれませんよ」と優しく教えてくれている証拠です。
皆さんのメインフレーム移行や保守作業が、少しでもスムーズに進みますように。また分からないことがあれば、いつでも聞きに来てくださいね。一緒に紐解いていきましょう。
