導入
C言語やC++でプログラミングを行う際、構造体のコピーは日常的な操作です。しかし、構造体の中にポインタ変数(動的に確保したメモリへの参照)が含まれている場合、標準的な代入演算子(=)だけでは期待通りの動作をしないことがあります。これを「浅いコピー(Shallow Copy)」と呼びます。もし、コピー元の構造体を解放した後にコピー先を参照すると、プログラムはメモリ不正アクセスを引き起こしてクラッシュします。本記事では、この課題を解決する「深いコピー(Deep Copy)」の実装手法を解説します。
基礎知識
構造体を代入すると、メンバ変数はそのままコピーされます。ポインタ変数の場合、コピーされるのは「データそのもの」ではなく「データが格納されているメモリアドレス」です。
結果として、コピー元とコピー先は「同じ場所」を指し示すことになります。一方がメモリを解放(free)すると、もう一方は「解放済みメモリを指すポインタ(ダングリングポインタ)」となり、非常に危険な状態になります。これを防ぐためには、新しくメモリ領域を確保し、中身を複製する「深いコピー」が必要です。
実装/解決策
深いコピーを実現するには、以下の手順を踏みます。
1. コピー先の構造体メンバに対して、コピー元が必要とするサイズのメモリを新たに確保(mallocなど)する。
2. 確保した領域に対し、memcpy関数などを用いてデータをコピーする。
3. ポインタ以外の単純なメンバ(整数型など)は、そのまま代入しても問題ありません。
サンプルプログラム
以下のコードは、動的に確保された配列を持つ構造体を安全にコピーする例です。
include
include
include
// ポインタを持つ構造体の定義
typedef struct {
int size;
int data; // 動的に確保される配列
} DataContainer;
int main() {
// 1. オリジナルの作成
DataContainer a;
a.size = 3;
a.data = (int )malloc(sizeof(int) a.size);
a.data[0] = 10; a.data[1] = 20; a.data[2] = 30;
// 2. 深いコピーの実行
DataContainer b;
b.size = a.size;
// 新たにメモリを確保して、データを複製する
b.data = (int )malloc(sizeof(int) b.size);
memcpy(b.data, a.data, sizeof(int) b.size);
// 3. 動作確認:個別に修正しても影響しない
b.data[0] = 999;
printf(“Original: %d, Copy: %d\n”, a.data[0], b.data[0]); // 10と999が出力される
// 4. メモリ解放(それぞれ個別に行う必要がある)
free(a.data);
free(b.data);
return 0;
}
応用・注意点
現場でよくある失敗は、「コピー元のメモリ解放を忘れる」ことや、逆に「二重に解放してしまう」ことです。構造体自体がスコープを抜ける際、ポインタメンバが指す領域も適切に管理されているか、常に意識してください。
もしC++を利用しているのであれば、コピーコンストラクタや代入演算子のオーバーロードを定義することで、このプロセスを自動化できます。構造体の設計時には「このデータは誰が所有権を持つのか」を明確に定義することが、バグのない堅牢なシステム構築への近道です。

コメント