1. 導入:なぜこの問題が重要なのか
メインフレーム(PL/I等)で開発をしていると、メモリ上の特定のアドレスを取得したい場面でADDR関数を多用しますよね。しかし、ビット型(BIT)の変数を扱う際、特に「UNALIGNED(非境界合わせ)」属性が付いていると、ADDR関数でエラーや予期せぬ挙動に悩まされることがあります。これは、コンピュータが「バイト」単位でしかメモリを管理できないという根本的な仕組みに起因しています。本記事では、この制限の正体と、現場で使えるスマートな解決策を解説します。
2. 基礎知識:なぜビット単位のアドレスは存在しないのか
コンピュータのメモリは、8ビット=1バイトを最小の管理単位としています。そのため、特定の「ビット」そのものを指す物理的なアドレスは存在しません。
PL/Iなどで「BIT(1) UNALIGNED」と宣言すると、コンパイラはメモリを節約するために、バイトの隙間を埋めるようにビットを詰め込みます。この状態でADDR関数を呼ぶと、コンパイラは「ビットそのもののアドレス」を返せないので、そのビットが含まれる「バイトの先頭アドレス」を返したり、コンパイルエラーを吐いたりします。これが「ポインタとロケータの不整合」問題の正体です。
3. 実装/解決策:どう対処すべきか
この制限を回避するには、主に2つのアプローチがあります。
1つ目は、「バイト境界に揃える(ALIGNED)」ことです。メモリ消費は増えますが、ADDR関数が正しく機能します。
2つ目は、「構造体でバイト単位にラップする」ことです。ビット操作が必要な部分だけを定義し、アドレス取得はバイト単位で行うように設計を変更します。
4. サンプルプログラム:安全なビット操作の例
以下は、直接ADDR関数を使わずに、ビット位置を管理してアクセスする手法のサンプルコードです。
/ 構造体でビットを管理する例 /
DCL 1 MY_DATA,
2 BYTE_VAL BIT(8) ALIGNED, / バイト境界に配置 /
2 BIT_OFFSET FIXED BIN(7); / ビット位置を別途管理 /
/ 特定のビットを操作するサブルーチン的な考え方 /
/ 実際には以下のようにマスク処理を行います /
/ マスク用の定数定義 /
DCL MASK_BIT1 BIT(8) INIT(‘10000000’B);
/ 処理ロジック /
/ 対象バイトのアドレスを取得(これはADDR可能) /
DCL PTR_TO_BYTE POINTER;
PTR_TO_BYTE = ADDR(MY_DATA.BYTE_VAL);
/ ビット操作は論理演算で行うのがメインフレーム流 /
/ 目的のビットをONにする処理 /
MY_DATA.BYTE_VAL = MY_DATA.BYTE_VAL | MASK_BIT1;
5. 応用・注意点:現場でのバグ回避
現場で最も多いバグは、UNALIGNEDの変数をポインタ経由で無理やり書き換えようとして、隣接する別の変数の値まで壊してしまうケースです。
補足アドバイス:
・どうしてもビット単位のアドレスが必要な場合は、ポインタ単体ではなく「ポインタ+オフセット」のペア構造体を定義し、プログラム全体でそのルールを徹底してください。
・可能な限り、ビット演算(AND, OR, XOR)で解決し、ADDR関数への依存を減らすことが、保守性の高いコードを書くコツです。
・古いコードを移行する際は、メモリマップを書き出して、どのバイトの何ビット目を指しているのかをドキュメント化しておくことを強く推奨します。

コメント