「参照型変数の引数」と「参照渡し」の違い

たびたび混同しているのを見かけるため改めてまとめておきます。

# 注釈

サンプルコードはPHPで書いてありますが、参照渡しなどの機能が一通りそろっていて、かつ気軽に試せる言語だったので採用しています。

C#やJavaなど、他の言語であっても基本的な考え方は似ています。
またC/C++のポインタを学ぶ上でもヒントになると考えています。

ただし言語によって、何が参照型で何が値型なのかが異なっていたり、そもそもどちらかの概念や機能がなかったりなど差分はあります。
そのあたりは各言語のマニュアルを確認していただくのが良いと思います。
マニュアルを確認する際の参考にもしてください。

PHPを普段使っていない方でも、サンプルコードは雰囲気で追えるはずです。
PHPを全く知らない方でもコードが読みやすくなるように「PHPの変数名は`$a`のように$マークから始まる」ということだけ説明しておきます。

# 解説

文章で解説するよりも、実際のコードを追っていただいた方が理解できると思います。
下のコードをコピペしてコマンドで実行すると結果が出ます。
※解説のためのサンプルなのでコードフォーマットとかは気にしないでください

```
$ php sample.php
```

```sample.php
<?php

// オブジェクトを1行に展開(本題とは関係なし)
function inline($obj) {
    return preg_replace('/\s+/', ' ', print_r($obj, true));
}

echo '* 値型変数の代入', PHP_EOL;

$a = 1;     echo '$a = 1;   $a: ', $a, PHP_EOL;
$b = $a;    echo '$b = $a;  $b: ', $b, PHP_EOL;
$a = 2;     echo '$a = 2;   $a: ', $a, PHP_EOL;
            echo '          $b: ', $b, '    $bの値は当然変わらない', PHP_EOL;

echo PHP_EOL;

echo '* 値型変数の参照の代入', PHP_EOL;

$a = 1;     echo '$a = 1;    $a: ', $a, PHP_EOL;
$b = &$a;   echo '$b = &$a;  $b: ', $b, '    $aへの参照を代入', PHP_EOL;
$a = 2;     echo '$a = 2;    $a: ', $a, PHP_EOL;
            echo '           $b: ', $b, '    $bの値は$aと連動する', PHP_EOL;
$b = 3;     echo '$b = 3;    $b: ', $b, PHP_EOL;
            echo '           $a: ', $a, '    $aの値も$bと連動する', PHP_EOL;

echo PHP_EOL;

// 一旦リセット
unset($a);
unset($b);

// 参照型変数の実験
class ClassA {
    public $v = 1;
}

echo '* 参照型変数の代入', PHP_EOL;

$a = new ClassA();  echo '$a = new ClassA();  $a: ', inline($a), PHP_EOL;
$b = $a;            echo '$b = $a;            $b: ', inline($b), PHP_EOL;
$a->v = 2;          echo '$a->v = 2;          $a: ', inline($a), PHP_EOL;
                    echo '                    $b: ', inline($b), '  参照先のオブジェクトが同一なため$b->vの値も連動する', PHP_EOL;
$b->v = 3;          echo '$b->v = 3;          $b: ', inline($b), PHP_EOL;
                    echo '                    $a: ', inline($a), '  逆も同様', PHP_EOL;
$a = new ClassA();  echo '$a = new ClassA();  $a: ', inline($a), PHP_EOL;
                    echo '                    $b: ', inline($b), '  変数$bは変数$aへの参照を持っているわけではないため連動しない', PHP_EOL;
                    echo '---- この時点で$aと$bは別のオブジェクトインスタンスを参照している状態になる', PHP_EOL;
$a->v = 4;          echo '$a->v = 4;          $a: ', inline($a), PHP_EOL;
                    echo '                    $b: ', inline($b), '  別のインスタンスなのでメンバも連動しない', PHP_EOL;
$b->v = 5;          echo '$b->v = 5;          $b: ', inline($b), PHP_EOL;
                    echo '                    $a: ', inline($a), '  逆も同様', PHP_EOL;

echo PHP_EOL;

echo '* 参照型変数の参照の代入', PHP_EOL;

$a = new ClassA();  echo '$a = new ClassA();  $a: ', inline($a), PHP_EOL;
$b = &$a;           echo '$b = &$a;           $b: ', inline($b), PHP_EOL;
$a->v = 2;          echo '$a->v = 2;          $a: ', inline($a), PHP_EOL;
                    echo '                    $b: ', inline($b), '  $b->vも連動していることが確認できる', PHP_EOL;
$b->v = 3;          echo '$b->v = 3;          $b: ', inline($b), PHP_EOL;
                    echo '                    $a: ', inline($a), '  逆も同様', PHP_EOL;
$a = new ClassA();  echo '$a = new ClassA();  $a: ', inline($a), PHP_EOL;
                    echo '                    $b: ', inline($b), '  変数$bは、変数$aと同じインスタンスを参照しているわけではなく、変数$aへの参照を持っている', PHP_EOL;
$a->v = 4;          echo '$a->v = 4;          $a: ', inline($a), PHP_EOL;
                    echo '                    $b: ', inline($b), '  当然メンバも連動する', PHP_EOL;
$b->v = 5;          echo '$b->v = 5;          $b: ', inline($b), PHP_EOL;
                    echo '                    $a: ', inline($a), '  逆も同様', PHP_EOL;
$b = new ClassA();  echo '$b = new ClassA();  $b: ', inline($a), PHP_EOL;
                    echo '                    $a: ', inline($b), '  $bに新しいインスタンスを代入しても同様', PHP_EOL;
                    echo '---- メンバに関しては以下略', PHP_EOL;

echo PHP_EOL;

// 一旦リセット
unset($a);
unset($b);

echo '* 関数の値型引数の値渡しと参照渡し', PHP_EOL;

function func1($val, &$ref) {
    $val = 5;
    $ref = 6;
}

$a = 1;         echo '$a = 1;         $a: ', $a, PHP_EOL;
$b = 2;         echo '$b = 2;         $b: ', $b, PHP_EOL;
func1($a, $b);  echo 'func1($a, $b);', PHP_EOL;
                echo '                $a: ', $a, '実引数$aは値渡しされているので変化しない', PHP_EOL;
                echo '                $b: ', $b, '実引数$bは参照渡しされているので変化する', PHP_EOL;

echo PHP_EOL;

echo '* 関数の参照型引数の値渡しと参照渡し', PHP_EOL;

function func2(ClassA $val, ClassA &$ref) {
    $val->v = 5;
    $ref->v = 6;
}

$a = new ClassA();  echo '$a = new ClassA();  $a: ', inline($a), PHP_EOL;
$b = new ClassA();  echo '$b = new ClassA();  $b: ', inline($b), PHP_EOL;
func2($a, $b);      echo 'func2($a, $b);', PHP_EOL;
                    echo '$val->v = 5;        $a: ', inline($a), '参照型変数のため値渡しでもメンバの値が変化する', PHP_EOL;
                    echo '$ref->v = 6;        $b: ', inline($b), '参照渡しでも同様の結果となる', PHP_EOL;

echo PHP_EOL;

echo '* 関数の参照型引数の値渡しと参照渡し その2', PHP_EOL;

function func2_2(ClassA $val, ClassA &$ref) {
    $val = new ClassA();
    $ref = new ClassA();
}

$a = new ClassA(); $a->v = 5;   echo '$a = new ClassA();  $a->v = 5;    $a: ', inline($a), PHP_EOL;
$b = new ClassA(); $b->v = 6;   echo '$b = new ClassA();  $b->v = 6;    $b: ', inline($b), PHP_EOL;
func2_2($a, $b);                echo 'func2_2($a, $b);', PHP_EOL;
                                echo '$val = new ClassA();              $a: ', inline($a), '参照型変数であっても値渡しでは実引数は書き換えられない', PHP_EOL;
                                echo '$ref = new ClassA();              $b: ', inline($b), '実引数への参照が渡っているため実引数そのものを書き換えられる', PHP_EOL;
```