初めに
PHPの引数の渡し方には、「値渡し」と、「参照渡し」と呼ばれるものがあるらしいです。(最近知った)
なので今回は、参照渡しについて詳しく理解した上で、実際に使ってみたいなーと思っています。
PHPのマニュアルでは参照のことをリファレンスと表記していますが、参照と記載したほうがわかりやすいので、ここでは参照と統一して記載します。
##参照渡しとは
引数の渡し方の一つであり、マニュアルにはこう書かれています。
PHP において、リファレンスとは同じ変数の内容を異なった名前で コールすることを意味します。これは C のポインタとは異なります。 リファレンスを使ってポインタの演算をすることはできませんし、 リファレンスは実メモリのアドレスでもありません。詳細は リファレンスが行わないこと を参照ください。 そうではなく、リファレンスはシンボルテーブルのエイリアスです。 PHP では、変数名と変数の内容は異なっており、 このため、同じ内容は異なった複数の名前を有する事が可能であることに 注意してください。最も良く似ているのは、Unix のファイル名とファイルの 関係です。この場合、変数名はディレクトリエントリ、変数の内容は ファイル自体に対応します。リファレンスは、Unix ファイルシステムの ハードリンクのようなものであると考えられます。
他のサイトや記事を元に、値渡しと比較してわかりやすく説明すると、
-
「値渡し (call by value)」
とは、変数の値をコピーする渡し方。 -
「参照渡し (call by reference)」
とは、変数を共有するような渡し方。
ということだそう。
もう少し詳しく説明すると、、、
例えば。
変数「x」には「10」という値が入っていて、
変数「y」には「20」という値が入っているとしよう。
まずはこの場合の変数の中身がどうなっているのかを知る必要がある。
$x = 10;
$y = 20;
変数の実体は、メモリ上に確保された領域であり、この場合だと
変数 x => 領域:001231番地 | 中身:10
変数 y => 領域:001232番地 | 中身:20
といったように、
- 変数 x はメモリ番地 001231番地 上の領域であり、値として数値の「10」が格納されている
- 変数 y はメモリ番地 001232番地 上の領域であり、値として数値の「20」が格納されている
(今回はわかりやすく番地で例えています。)
この辺を理解した上でまとめると、
値渡し
- 番地の中身をコピーする
- 上書きされても影響を受けない(コピーしているため)
参照渡し
- メモリ領域を参照する
- 参照元が上書きされると参照先も影響を受ける
実際に書いて確かめよう!
まずは値渡しから。
$a = 1;
$b = $a; //$bは、$a = 1 時点での $a の値をコピーしている。
$a = 100 //なので、ここで $a を書き換えても、$b は影響を受けない。
echo $a;
echo '<br>';
echo $b;
// 出力内容
// 100
// 1
ではお次。参照渡し。
注)参照渡しを行う場合は、&
でつなげる。
$a = 1;
$b =& $a; //ここで $b は、 $a のメモリ領域を参照している
$a = 100 //$a を書き換えると $b は $a のメモリ領域を参照しているので、
echo $a;
echo '<br>';
echo $b;
// 出力内容
// 100
// 100 //$b は $a と同じ値を出力する。
$a
の値を変更すると、$b
もその影響を受けていることが確認できる。
これは、お互いがお互いを参照しているということになるので、その後に$b = 333
というように書き換えると、$a, $b
両方とも値は333
という結果になる。
関数で利用してみる
値渡し
function ReferenceTest($item) {
return $item += 999;
}
$a = 1;
$b = ReferenceTest($a);
echo $a;
echo '<br>';
echo $b;
// 出力結果
// 1
// 1000
参照渡し
function ReferenceTest(&$item) {
return $item += 999;
}
$a = 1;
$b = ReferenceTest($a); // ここで引数に入った $a は $item と互いに参照する
echo $a;
echo '<br>';
echo $b;
// 出力結果
// 1000
// 1000
上記の結果をまとめると、ReferenceTest()
に$a
を入れた時点で、$item
が$a
のメモリ領域を参照していることになる。
なので、$a
は、$item
に格納されたメモリ領域を辿って結果を出力している。
同じ出力結果でわかりづらいが、決して$b
を参照しているわけではない。
$b
は$a
の値をコピーした「値渡し」である。
次の結果を見ればよくわかるはず。
function ReferenceTest(&$item) {
return $item + 999; // 変更箇所
}
$a = 1;
$b = ReferenceTest($a);
echo $a;
echo '<br>';
echo $b;
// 出力結果
// 1
// 1000
上記処理は、ただ計算結果(1000という数字)を返しただけであり、変数自体を書き換えているわけではない。
その結果、$b = 1000
になるが、$a
は$b
を参照しているわけではないので影響を受けていない。
$a
はそのままの値$a = 1
を出力している。
(僕の方がこの記事書きながらこんがらがったので二つの処理を分けて書きました。。。)
foreachで回してみよう!
値渡し
$arrayNumber = [1, 2, 3, 4, 5];
foreach ($arrayNumber as $squareNumber) {
$squareNumber *= $squareNumber;
}
echo '<pre>';
print_r($arrayNumber);
echo '</pre>';
// 出力結果
// Array
// (
// [0] => 1
// [1] => 2
// [2] => 3
// [3] => 4
// [4] => 5
// )
//
上記の場合は「値渡し」なので、$arrayNumber
の値を書き換えることはできない。
参照渡し
$arrayNumber = [1, 2, 3, 4, 5];
foreach ($arrayNumber as &$squareNumber) {
$squareNumber *= $squareNumber;
}
echo '<pre>';
print_r($arrayNumber);
echo '</pre>';
// 出力結果
// Array
// (
// [0] => 1
// [1] => 4
// [2] => 9
// [3] => 16
// [4] => 25
// )
//
上記処理は、foreach
で要素変数を参照渡ししている。
その結果、$squareNumber
は、$arrayNumber
の各要素のメモリ領域を参照するので、$arrayNumber
の中身を綺麗に書き換えることに成功した。
まとめ
参照渡し
○メモリの領域を互いに参照する。僕の感覚では値の居場所を特定している感じ。
○コピーではないので上書きをすれば影響を受ける(一心同体的な)。
値渡し
○値をコピーする。
○コピーしてあるので影響を受けない。
このぐらい把握していれば今回の内容は理解できたと思います。(偉そうにしていますが間違っていたらしばいて下さい)
今回は、最近知った参照渡しを遊び感覚で使ってみましたが、メモリの節約だったりだとか、長所と短所を生かした使い分けなどに関してはまだ慣れていないので、これから実務の中でも必要に応じて使い分けられるようになりたいな