1. 導入:なぜ「一括代入」が危険なのか
メインフレームのPL/I開発において、構造体や配列の一括代入は記述を簡潔にする便利な手法です。しかし、そこに「ポインタ(ロケータ変数)」が含まれている場合、思わぬ副作用が生じます。アドレスだけがコピーされ、実体は共有されたままになる「浅いコピー(Shallow Copy)」の特性により、二重解放(Double Free)や意図しないデータの書き換えが頻発します。本記事では、このリスクを正しく理解し、安全なメモリ管理を行うためのTipsを解説します。
2. 基礎知識:ロケータ変数とメモリ管理の仕組み
PL/Iにおけるロケータ変数は、特定のメモリ領域を指し示す「アドレス」を保持する変数です。
一括代入の仕組み:構造体全体を代入(例:A = B)すると、各メンバはそのままコピーされます。このとき、ポインタ型のメンバは「指し示しているアドレス値」がコピーされます。
問題点:コピー元とコピー先が「同じメモリ領域」を指している状態になります。片方の構造体で領域を解放(FREE)すると、もう片方は「無効な領域」を指すことになり、後の処理で異常終了やデータ破壊を引き起こします。
3. 実装/解決策:個別の領域確保とディープコピー
一括代入に頼らず、ポインタが含まれる場合は、新しい領域を確保してから内容を転記する「ディープコピー」が基本原則です。
4. サンプルプログラム:安全な構造体コピーの実装例
以下は、ポインタを含む構造体を安全にコピーするための実装例です。
/ 構造体定義:ポインタメンバを含む /
DECLARE 1 STRUCT_TYPE BASED(P_STRUCT),
2 DATA_KEY CHAR(8),
2 P_DATA POINTER;
DECLARE (P_SRC, P_DST) POINTER;
DECLARE P_DATA_SRC POINTER BASED(P_SRC),
P_DATA_DST POINTER BASED(P_DST);
/ 安全なコピー処理のロジック /
/ 1. コピー先の構造体本体を確保 /
ALLOCATE STRUCT_TYPE SET(P_DST);
/ 2. キー部分は一括代入しても問題なし /
STRUCT_TYPE(P_DST).DATA_KEY = STRUCT_TYPE(P_SRC).DATA_KEY;
/ 3. ポインタメンバは「新しい領域」を確保してから値を転記する /
ALLOCATE DATA_AREA SET(STRUCT_TYPE(P_DST).P_DATA);
STRUCT_TYPE(P_DST).P_DATA->DATA_AREA = STRUCT_TYPE(P_SRC).P_DATA->DATA_AREA;
/ これにより、P_DSTを解放してもP_SRC側には影響を与えない /
5. 応用・注意点:現代言語への移行を見据えて
現場でよく見られる「コピー後に個別に書き換える」手法は、Java等のオブジェクト指向言語へ移行する際、参照の共有によるバグを誘発します。
注意点:
・二重解放の防止:FREE命令を発行する際は、必ず有効なポインタかを確認し、解放後はNULL(またはヌルポインタ)を代入して無効化する習慣をつけましょう。
・設計の工夫:構造体が複雑な場合は、コピー専用のサブプロシージャを作成し、メンバごとの管理責任を明確にすることが、保守性を高める唯一の近道です。
・クローンパターンの意識:Java等への将来的な移植を考慮し、「深いコピー(Deep Copy)」を作成するメソッドを意識したコーディングを心がけてください。

コメント