MENU

プログラミングの値型と参照型とは?違いを初心者向けに徹底解説

プログラミングを学んでいると、「値型」と「参照型」という言葉を耳にすることがあります。この2つの違いを理解することは、予期せぬバグを防ぎ、効率的なコードを書くために非常に重要です。この記事では、値型と参照型の違いを初心者の方にもわかりやすく、具体例とともに解説します。

目次

値型と参照型とは

プログラミングでは、データの扱い方によって**値型(Value Type)参照型(Reference Type)**の2つに分類されます。

簡単に言うと、

  • 値型:変数にデータそのものが入っている
  • 参照型:変数にデータの場所(住所)が入っている

この違いは、変数をコピーしたり、関数に渡したりするときの動作に大きく影響します。

値型(Value Type)の基本

値型は、変数にデータの実体そのものが格納される型です。

値型の特徴

  1. 変数にデータが直接入っている
  2. 代入時にデータ全体がコピーされる
  3. それぞれが独立しているため、一方を変更しても他方に影響しない

値型の具体例

日常生活に例えると、値型は「メモ用紙にメッセージを書いてコピーする」ようなものです。コピーした紙に何を書いても、元の紙には影響しません。

// Swiftの例
var a = 10
var b = a  // aの値(10)がbにコピーされる

b = 20     // bだけを変更

print(a)   // 10(変更されていない)
print(b)   // 20

この例では、aの値がbにコピーされるため、bを変更してもaには影響しません。

JavaScriptでの値型の例

// プリミティブ型は値型
let x = 5;
let y = x;  // xの値がyにコピーされる

y = 10;     // yだけを変更

console.log(x);  // 5(変更されていない)
console.log(y);  // 10

各言語における主な値型

Swift

  • Int, Double, Float, Bool
  • String
  • Array, Dictionary, Set
  • Struct(構造体)
  • Enum(列挙型)
  • Tuple(タプル)

JavaScript

  • number
  • string
  • boolean
  • null
  • undefined
  • symbol
  • bigint

C#

  • int, float, double, bool
  • struct(構造体)
  • enum(列挙型)

Java

  • int, double, float, boolean
  • char, byte, short, long

参照型(Reference Type)の基本

参照型は、変数に**データの場所(参照・アドレス)**が格納される型です。

参照型の特徴

  1. 変数にはデータの参照(メモリアドレス)が入っている
  2. 代入時に参照がコピーされる(データ自体はコピーされない)
  3. 複数の変数が同じデータを指すため、一方を変更すると他方にも影響する

参照型の具体例

日常生活に例えると、参照型は「Googleドキュメントのリンクを共有する」ようなものです。リンクをコピーしても、全員が同じドキュメントを見ているため、誰かが編集すると全員に反映されます。

// Swiftの例
class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

var person1 = Person(name: "太郎")
var person2 = person1  // person1の参照がperson2にコピーされる

person2.name = "花子"  // person2を通じてデータを変更

print(person1.name)    // "花子"(変更されている!)
print(person2.name)    // "花子"

この例では、person1person2は同じPersonオブジェクトを指しているため、どちらを変更しても両方に影響します。

JavaScriptでの参照型の例

// オブジェクトは参照型
let obj1 = { name: "太郎" };
let obj2 = obj1;  // obj1の参照がobj2にコピーされる

obj2.name = "花子";  // obj2を通じてデータを変更

console.log(obj1.name);  // "花子"(変更されている!)
console.log(obj2.name);  // "花子"

各言語における主な参照型

Swift

  • Class(クラス)
  • Function(関数)
  • Closure(クロージャ)

JavaScript

  • Object(オブジェクト)
  • Array(配列)
  • Function(関数)
  • Date, RegExp など

C#

  • class(クラス)
  • string
  • array(配列)
  • delegate

Java

  • すべてのクラスのインスタンス
  • 配列
  • インターフェース

値型と参照型の動作を図で理解

値型の動作イメージ

ステップ1:変数aに値を代入

[a: 10]

ステップ2:aをbにコピー [a: 10] [b: 10] ← それぞれ独立したデータ ステップ3:bを変更 [a: 10] [b: 20] ← aは影響を受けない

参照型の動作イメージ

ステップ1:変数obj1を作成
[obj1] → {name: "太郎"}

ステップ2:obj1をobj2にコピー
[obj1] → {name: "太郎"} ← [obj2]
         両方が同じデータを指している

ステップ3:obj2を通じて変更
[obj1] → {name: "花子"} ← [obj2]
         どちらも同じデータを見ているため両方に影響

値型と参照型の比較表

項目値型参照型
格納内容データの実体データへの参照(アドレス)
コピー時データ全体をコピー参照のみコピー
変更の影響独立(影響しない)共有(影響する)
メモリ配置スタック領域(通常)ヒープ領域
パフォーマンス小さいデータでは高速大きいデータでは効率的
用途小さなデータ、不変値大きなデータ、共有が必要

配列の扱いに注意

配列の扱いは言語によって異なるため、注意が必要です。

Swift:配列は値型

var array1 = [1, 2, 3]
var array2 = array1  // 配列全体がコピーされる

array2.append(4)

print(array1)  // [1, 2, 3](変更されない)
print(array2)  // [1, 2, 3, 4]

JavaScript:配列は参照型

let array1 = [1, 2, 3];
let array2 = array1;  // 参照がコピーされる

array2.push(4);

console.log(array1);  // [1, 2, 3, 4](変更される!)
console.log(array2);  // [1, 2, 3, 4]

このように、同じ配列でも言語によって動作が異なります。

よくある落とし穴とその対策

落とし穴1:参照型の意図しない共有

// 問題のあるコード
let originalUser = { name: "太郎", age: 25 };
let copiedUser = originalUser;

copiedUser.age = 30;

console.log(originalUser.age);  // 30(意図せず変更されている!)

対策:シャローコピーまたはディープコピー

// シャローコピー(浅いコピー)
let copiedUser = { ...originalUser };

// または
let copiedUser = Object.assign({}, originalUser);

copiedUser.age = 30;

console.log(originalUser.age);  // 25(変更されない)
console.log(copiedUser.age);    // 30

落とし穴2:配列内の参照型オブジェクト

配列自体が値型でも、中身が参照型の場合は注意が必要です。

class Person {
    var name: String
    init(name: String) {
        self.name = name
    }
}

var people1 = [Person(name: "太郎")]
var people2 = people1  // 配列はコピーされる

people2[0].name = "花子"  // でも中のPersonオブジェクトは参照型

print(people1[0].name)  // "花子"(影響を受ける!)
print(people2[0].name)  // "花子"

対策:新しいインスタンスを作成

var people2 = people1.map { Person(name: $0.name) }

落とし穴3:関数の引数として渡す場合

function changeValue(num) {
    num = 100;
}

function changeObject(obj) {
    obj.value = 100;
}

let number = 10;
let object = { value: 10 };

changeValue(number);
changeObject(object);

console.log(number);        // 10(変更されない)
console.log(object.value);  // 100(変更される!)

値型と参照型の使い分け

値型を使うべき場合

小さなデータ

struct Point {
    var x: Int
    var y: Int
}

座標のような小さなデータは値型が適しています。

不変性を保ちたい

struct Color {
    let red: Int
    let green: Int
    let blue: Int
}

データを変更されたくない場合は値型が安全です。

独立性が必要

struct Settings {
    var theme: String
    var fontSize: Int
}

それぞれ独立した設定を持たせたい場合は値型が適しています。

参照型を使うべき場合

大きなデータ

class ImageData {
    var pixels: [UInt8]  // 大量のデータ
    // ...
}

大きなデータをコピーするコストを避けたい場合は参照型が効率的です。

データの共有が必要

class UserSession {
    var isLoggedIn: Bool
    var userId: String?
}

let session = UserSession()
// アプリ全体で同じセッション情報を共有

複数の場所で同じデータを参照したい場合は参照型が適しています。

継承を使いたい

class Animal {
    func makeSound() { }
}

class Dog: Animal {
    override func makeSound() {
        print("ワン")
    }
}

クラスの継承が必要な場合は参照型(Class)を使う必要があります。

言語別の特徴

Swift

Swiftは値型を重視した設計になっています。

  • 標準ライブラリのほとんどが値型(String, Array, Dictionaryなど)
  • Structを使った値型の設計が推奨される
  • Classは継承やデータ共有が必要な場合のみ使用

JavaScript

JavaScriptはプリミティブ型とオブジェクト型で明確に分かれています。

  • プリミティブ型(number, string, booleanなど)は値型
  • オブジェクト型(Object, Array, Functionなど)は参照型
  • スプレッド構文やObject.assignでシャローコピーが可能

Python

Pythonは少し特殊です。

  • すべてがオブジェクトで、基本的に参照が渡される
  • しかし、不変オブジェクト(int, str, tupleなど)は値型のように振る舞う
  • 可変オブジェクト(list, dict, setなど)は参照型として動作

Java

Javaは明確に区別されています。

  • プリミティブ型(int, double, booleanなど)は値型
  • すべてのオブジェクトは参照型
  • ラッパークラス(Integer, Doubleなど)も参照型

パフォーマンスへの影響

値型のパフォーマンス特性

メリット

  • スタックメモリに配置されるため、アクセスが高速
  • ガベージコレクションの対象にならない
  • 小さなデータでは効率的

デメリット

  • 大きなデータをコピーするとコストが高い
  • 頻繁にコピーが発生する場合はメモリ使用量が増える

参照型のパフォーマンス特性

メリット

  • 大きなデータでもコピーコストが低い(参照のみコピー)
  • メモリ使用量を抑えられる

デメリット

  • ヒープメモリに配置され、アクセスが若干遅い
  • ガベージコレクションの対象となる
  • 参照の管理が必要

実践的なコード例

値型を使った設計例

struct UserProfile {
    var username: String
    var email: String
    var age: Int
    
    mutating func updateEmail(newEmail: String) {
        self.email = newEmail
    }
}

var profile1 = UserProfile(username: "taro", email: "taro@example.com", age: 25)
var profile2 = profile1  // 独立したコピー

profile2.updateEmail(newEmail: "taro_new@example.com")

print(profile1.email)  // "taro@example.com"(変更されない)
print(profile2.email)  // "taro_new@example.com"

参照型を使った設計例

class ShoppingCart {
    var items: [String] = []
    
    func addItem(_ item: String) {
        items.append(item)
    }
    
    func removeItem(_ item: String) {
        items.removeAll { $0 == item }
    }
}

let cart = ShoppingCart()
let sameCart = cart  // 同じカートを参照

cart.addItem("リンゴ")
sameCart.addItem("バナナ")

print(cart.items)      // ["リンゴ", "バナナ"]
print(sameCart.items)  // ["リンゴ", "バナナ"](同じデータ)

まとめ

値型と参照型の違いは、プログラミングにおいて非常に重要な概念です。重要なポイントをおさらいしましょう。

値型の特徴

  • データそのものを格納
  • コピー時にデータ全体が複製される
  • 独立しているため、一方の変更が他方に影響しない
  • 小さなデータや不変性が必要な場合に適している

参照型の特徴

  • データへの参照(アドレス)を格納
  • コピー時に参照のみが複製される
  • 同じデータを共有するため、一方の変更が他方に影響する
  • 大きなデータやデータ共有が必要な場合に適している

実践のポイント

  • 使用している言語でどの型が値型・参照型かを理解する
  • 配列やオブジェクトをコピーする際は、値型か参照型かを意識する
  • 意図しないデータ共有を避けるため、必要に応じてディープコピーを行う
  • パフォーマンスと設計のバランスを考えて適切に使い分ける

この違いを理解することで、予期せぬバグを防ぎ、より効率的で保守性の高いコードを書くことができます。最初は難しく感じるかもしれませんが、実際にコードを書いて動作を確認しながら学んでいくことで、自然と身についていきます。

まずは小さなコード例で値型と参照型の動作の違いを試してみて、徐々に実践的なプログラムに応用していきましょう。

プログラミングの独学におすすめ
プログラミング言語の人気オンラインコース
独学でプログラミングを学習している方で、エラーなどが発生して効率よく勉強ができないと悩む方は多いはず。Udemyは、プロの講師が動画で実際のプログラムを動かしながら教えてくれるオンライン講座です。講座の価格は、セール期間中には専門書籍を1冊買うよりも安く済むことが多いです。新しく学びたいプログラミング言語がある方は、ぜひUdemyでオンライン講座を探してみてください。
目次