はじめに
プログラムでは、ある変数の内容を別の変数へコピーする処理を頻繁に行います。しかし、コピーした後の動きは、データの種類によって大きく変わります。例えば、数値をコピーした場合は元の変数とコピー先の変数は別々に扱われます。一方で、オブジェクトや配列などをコピーした場合、片方を変更するともう片方にも影響することがあります。
「コピーしただけなのに、なぜ元のデータまで変わってしまうのか」
このような問題の背景にあるのが、値型と参照型の違いです。値型と参照型の違いを理解することは、プログラムの不具合を防ぐだけでなく、メモリの使われ方やデータ管理の考え方を理解するうえでも重要です。
この記事では、値型と参照型について、コピー時に何が複製されるのか、なぜ動作に違いが出るのかを図を使って解説します。
値型とはデータそのものを保持する型
値型とは、変数自身がデータそのものを保持する仕組みです。
例えば、変数Aに「10」という数値を保存した場合、変数Aの中には10という値そのものが格納されています。ここで変数Aを変数Bへコピーすると、コピーされるのはデータそのものです。

(図1:値型のコピーの仕組み)
※図では、変数Aと変数Bがそれぞれ別々の値を保持していることを示す。
コピー後は、変数Aと変数Bは独立した状態になります。そのため、変数Bの値を変更しても、変数Aには影響しません。例えば以下のような処理を考えます。
A = 10
B = A
B = 20
この場合、最終的な状態は以下になります。
A → 10
B → 20
変数Aと変数Bは、それぞれ別のデータを持っているためです。代表的な値型には、整数や小数、真偽値などがあります。
参照型とはデータの場所を保持する型
参照型は、値型とは考え方が異なります。
参照型では、変数の中にデータそのものを保存するのではなく、データが存在する場所を示す情報(参照)を保存します。例えば、ユーザー情報や商品情報のような複数のデータを持つオブジェクトを考えてみます。変数には以下のような情報が入っています。
変数A
↓
データが存在する場所
↓
ユーザー情報
この状態で変数Aを別の変数Bへコピーすると、コピーされるのはデータ本体ではなく、参照先の情報です。

(図2:参照型のコピーの仕組み)
※図では、変数Aと変数Bが同じデータ本体を参照していることを示す。
つまり、コピー後は変数Aと変数Bは別々の変数ですが、見ているデータは同じ状態になります。
なぜコピーしたのに元のデータが変わるのか
参照型で混乱しやすいポイントが、「コピー後の変更」です。例えば、ユーザー情報を保存しているデータがあるとします。
名前:田中
年齢:30歳
このデータを別の変数へコピーした後、変数Bから年齢を31歳へ変更したとします。参照型では、変数Aと変数Bは同じデータ本体を参照しています。そのため、結果は以下のようになります。
変数A
名前:田中
年齢:31歳
変数B
名前:田中
年齢:31歳
「変数Bだけ変更したつもりなのに、変数Aも変わっている」
これはプログラムの異常ではなく、参照型の仕組みによる正常な動作です。
関数へデータを渡す場合にも違いが出る
値型と参照型の違いは、変数のコピーだけではありません。関数やメソッドへデータを渡す場合にも影響します。値型では、基本的にデータのコピーが渡されます。そのため、関数内部で値を変更しても、呼び出し元の変数には影響しません。
一方、参照型では参照情報が渡されるため、関数内部でデータ本体を変更すると、呼び出し元にも影響する場合があります。例えば、ユーザー情報を変更する処理を関数化した場合、意図せず元のデータを書き換えてしまう可能性があります。このため、プログラム設計では「このデータは変更してよいものなのか」「コピーして独立させる必要があるのか」を意識することが重要です。
参照型で注意したいシャローコピーとディープコピー
参照型では、単純なコピーではデータが共有される場合があります。このようなコピーを一般的にシャローコピー(浅いコピー)と呼びます。シャローコピーでは、参照先の情報だけがコピーされるため、コピー後も同じデータを利用します。
一方、データ本体まで複製する方法をディープコピー(深いコピー)と呼びます。ディープコピーでは、コピー元とコピー先が完全に別のデータになります。
例えば、元の商品情報を保持したまま、コピーした商品情報だけ変更したい場合には、ディープコピーが必要になることがあります。ただし、具体的なコピー方法はプログラミング言語や利用する仕組みによって異なるため、開発環境の仕様を確認する必要があります。
値型と参照型を理解するメリット
値型と参照型の違いを理解すると、プログラムで発生する予期しない動作の原因を追いやすくなります。特に、以下のような場面では意識することが重要です。
複数の場所で同じデータを利用する場合、参照型では変更の影響範囲を確認する必要があります。
また、大量のデータを扱う場合には、コピーによるメモリ使用量や処理速度への影響も考える必要があります。安全性だけを考えてすべてコピーすると、不要な処理負荷につながる場合があります。
逆に、データ共有を意識せず参照型を利用すると、原因を特定しにくい不具合につながることがあります。重要なのは、値型と参照型のどちらが優れているかではなく、データの扱い方に応じて使い分けることです。
まとめ
値型と参照型の違いは、「コピーすると何が複製されるのか」を理解すると整理できます。
値型では、データそのものがコピーされるため、コピー元とコピー先は独立します。一方、参照型では、データが保存されている場所を示す情報がコピーされるため、同じデータを共有します。そのため、参照型ではコピー後の変更が元のデータへ影響する場合があります。
プログラムで発生する「なぜかデータが変わっている」という問題の多くは、この仕組みを理解することで原因を追いやすくなります。単なる用語として覚えるのではなく、メモリ上でどの情報がコピーされているのかを意識することが、品質の高いプログラムを書くための基本になります。

コメント