人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

PHPの参照代入に関してです。
PHPを勉強し始めたのですが、RubyやPythonといった言語と変数・配列の扱いが異なって戸惑っています。
一番わからないのが参照代入です。
http://www.programming-magic.com/20080307090613/
このサイトで挙動が解説されてますが、イマイチ納得できません。
PHPの変数の参照代入に関して詳しく解説しているサイトを教えてください。

●質問者: kabisuke
●カテゴリ:コンピュータ ウェブ制作
○ 状態 :終了
└ 回答数 : 1/1件

▽最新の回答へ

1 ● パパトモ
●300ポイント ベストアンサー

あー、これ分かりにくいね。

$array[0] = 1;
$array[1] = 2;
$array[2] = 3;


ここは問題ないね。
ポイントは次

$ref = &$array[1];


この式を抜けた結果、$refはリファレンス(ポインタ/参照)に、そして$array[1]もリファレンス(ポインタ/参照)に変化すると考えると分かりやすいかも。

つまりこの段階でこんな感じになっている

$array[0] = 1;
$array[1] → とある変数空間 ← $ref
$array[2] = 3;


そして次

$copy = $array;


この直後は

$copy[0] = 1;
$copy[1] → とある変数空間 ← $array[1]
$copy[2] = 3; ↑
$ref


こんな感じ。

なので

$copy[0] = 'a';
$copy[1] = 'b';
$copy[2] = 'c';


と実行すると、「とある変数空間」の中身が「b」となって、結果、$array[1]が「b」になるということ。

これ知らなかったけど、これでいいのかPHPって感じの仕様ですね。

http://jp2.php.net/manual/ja/language.references.whatdo.php


パパトモさんのコメント
おっと、これだと、コメントの例題の説明つかないね

パパトモさんのコメント
ああ、分かったPHPの配列は、もともとリファレンスなんだね。 上手い説明できそうな気がするので、ちょっとまっててね。

パパトモさんのコメント
リンク先の例の $copy = $array; と、コメントの例題2の $b = $a; はメモリ空間的には等価じゃないってところがポイント。配列の場合は元々リファレンスだから、配列ではない変数の場合、メモリ空間的に同じ意味にするには、次のように書かなきゃダメというか、こういうこと $b = &$a; 配列と変数を同じように考えたのが間違いの元でしたということ。

パパトモさんのコメント
PHP大好きの人には申し訳ないが、PHPはちょっと好きになれないわ〜〜

TransFreeBSDさんのコメント
>> 配列の場合は元々リファレンスだから << 分かるような分からないようなw >|| $copy = $array; $copy = &$array; $copy[0] = $array[0]; $copy[1] = $array[1]; $copy[0] = &$array[0]; $copy[1] = &$array[1]; ||< 全部挙動が違うという。 http://ideone.com/EOpIA たぶん >> $array[0] = 1; $array[1] → とある変数空間 ← $ref $array[2] = 3; << のコピーは >> $copy[0] = 1; $copy[1] → とある変数空間 ← $ref $copy[2] = 3; << だろう?っていう感じだと思うけれど。 $ref = &$a; ってのが実質副作用無い(たぶん)のに $ref = &$array[1]; に副作用あるってのがなー http://jp2.php.net/manual/ja/language.references.whatdo.php >> 通常の (リファレンスではない) 代入の右辺にリファレンスを使っても 左辺はリファレンスには変わりませんが、配列の内部のリファレンスは通常の代入のままとなります。 << 「通常の代入」って意味わかんないですよw

パパトモさんのコメント
まず配列はリファレンスの集合体だから >> $array[0] = 1; $array[1] = 2; $array[2] = 3; << の意味は・・・ >> $array[0]= メモリ空間1へのリファレンス → メモリ空間1; $array[1]= メモリ空間2へのリファレンス → メモリ空間2; $array[2]= メモリ空間2へのリファレンス → メモリ空間3; さらに処理は続いて メモリ空間1=1 メモリ空間2=2 メモリ空間3=3 << これは問題ない? そして次 >> $ref = &$array[1]; << これを言い換えると・・・ >> まずは右辺を左辺に代入 $ref = メモリ空間2へのリファレンス 次は右辺のリファレンス化 $array[1] = メモリ空間2へのリファレンスへのリファレンス → メモリ空間2へのリファレンス → メモリ空間2 << ああ、だんだんややこしくなってきた! そして次 >> $copy = $array; << これを言い換えると >> $copy[0]= メモリ空間New1へのリファレンス → メモリ空間New1; $copy[1]= メモリ空間New2へのリファレンス → メモリ空間New2; $copy[2]= メモリ空間New3へのリファレンス → メモリ空間New3; さらに処理は続いて メモリ空間New1=1 メモリ空間New2=メモリ空間2へのリファレンス メモリ空間New3=3 << ため息出る〜〜 >> $copy[0] = 'a'; $copy[1] = 'b'; $copy[2] = 'c'; << ということで、上の意味は・・・ >> メモリ空間New1= a メモリ空間2= b メモリ空間New3= c << この時点で、arrayの指す変数空間は >> メモリ空間1=1 メモリ空間2=b メモリ空間3=3 << ここまで、いいかな? ところで、例題2はというと >> $tmp = &$a; << ここがポイントなので、ここだけ翻訳 >> まずは右辺を左辺に代入 $tmp =メモリ空間a 次は右辺のリファレンス化 $a= メモリ空間aへのリファレンス → メモリ空間a << ここまで書けば分かると思う・・・ お腹減ったっす。 (ゴメン、コメント2回修正した。腹へって死にそう・・・飯を食いにいってきます)

パパトモさんのコメント
ちなみに配列はリファレンスの集合体というのは、PHPに限らず、大抵の言語はそうなっていると思います。

パパトモさんのコメント
補足です。何度も何度も書き直しているようでゴメンなさい。 昼飯から戻って改めて見ると、ちょっと分かりにくい所がありますね。 >> $変数=メモリ空間α << の意味は $変数に「メモリ空間αの値を代入(コピー)」の意味です。 $変数にメモリ空間αを割り当てるという意味ではないので、誤解の無いように。 この辺りは「配列とリファレンスの奇妙な動作【PHP】」に書かれていることの復唱でしかありません。

TransFreeBSDさんのコメント
参照代入で両辺がリファレンスに変換され、リファレンスが評価されると自動的にデリファレンスされるが、配列自体のコピーではデリファレンスされることなくリファレンス自身がコピーされる、と理解しました。 関数の引数処理やオブジェクトの関係でこんな仕様になってるんですかね。 ところで。 配列がリファレンスの集合体と言うのはどうかなと思います。 Cは連続した記憶域そのものですし、perlも実装はともかく連続した変数領域と見て良かったと思います。 pythonはそもそも変数自体がリファレンスですし。 というのは脇道ですけど。

kabisukeさんのコメント
パパトモさん、回答&長文に渡るコメント、ありがとうございます。 読ませてもらい、いろいろ考えたり実験した結果、 TransFreeBSDさんの言うように、 通常変数の代入の場合: 両辺がリファレンスになったあと、両辺デリファレンスされる 配列の要素の代入の場合: 両辺がリファレンスになったあと、左辺の変数だけデリファレンスされる と考えるのが分かりやすそうです。 ちょっとphp -aで実験です。 まず、&を使うと参照型になるってこと。 >> php > $a = 1; php > var_dump($a); int(1) php > var_dump(&$a); &int(1) << でも、通常の参照代入で値が変化することはない。 ただ、おなじモノを指していることにはなる。 解釈としては変数に別名をあたえた、みたいな感じですかね。 ここまではわかりやすい。 >> php > $a = 1; php > $b = &$a; php > var_dump($a); int(1) php > var_dump($b); int(1) php > $b = 2; php > var_dump($a); int(2) php > var_dump($b); int(2) << 本題はここから。 配列も作っただけなら中身は参照型ではない。 &をつけるだけなら別に中身が変わることはない。 >> php > $arr = array(1); php > var_dump($arr); array(1) { [0]=> int(1) } php > var_dump($arr[0]); int(1) php > var_dump(&$arr[0]); &int(1) php > var_dump($arr[0]); int(1) << ここで、配列の要素の代入を行うと、 その代入を行った要素が参照型に変化してしまう。 しかも、一度参照型になってしまうと、 再度その要素に代入しても参照型のままになってしまう。 >> php > $arr = array(1); php > $tmp = &$arr[0]; php > var_dump($arr); array(1) { [0]=> &int(1) } php > var_dump($tmp); int(1) php > $arr[0] = 'a'; php > var_dump($arr); array(1) { [0]=> &string(1) "a" } << では、配列の要素を配列の要素に参照代入すると? このときは代入された配列の中身も参照型になる。 >> php > $arr1 = array(1); php > $arr2 = array('a'); php > $arr2[0] = &$arr1[0]; php > var_dump($arr1); array(1) { [0]=> &int(1) } php > var_dump($arr2); array(1) { [0]=> &int(1) } << では、代入される側が配列の要素で、 代入する側が普通の変数の場合は。 >> php > $arr = array(1); php > $a = 'a'; php > $arr[0] = &$a; php > var_dump($a); string(1) "a" php > var_dump($arr); array(1) { [0]=> &string(1) "a" } << やはり参照型で代入されている。 結局何だったのか、というと、 PHPは参照操作をするときは一旦参照型にして演算するけど、 最後に参照型から通常型に戻そうとする。(デリファレンス) でも、配列の要素までは追っかけてデリファレンスできなくて、 そのまま参照型が残り続ける。 なんてところだと予想されます。 ---- なんでこんなことに、と思ってしまいますが、 多分変数自体には「参照型」を作りたくなかったのではないか、と思います。 どんな変数も「箱」のようなもので、値を持っている。 なので、変数に参照型の値を代入しようとしても、基本的にはデリファレンスしてしまう。 デリファレンスの結果、同じ「箱」に別名がついたりする。 配列も、中身の実装はどうなってるかは置いておいて、 「箱」のなかに「箱」があるイメージで扱えるようにしておきたい、 というのがおそらく基本スタイルなんでしょう。 だから通常の配列の代入はコピー。 そこで、配列の要素である、「箱のなかの箱」を参照型にしたあと デリファレンス(つまり別名をつける)しようとしても、 「箱のなかの箱」には名前をつけることができない。 だから仕方なく「参照型」ということにしておく。 つまり、デリファレンスを後回しにしている感じですね。 この「後回し」が奇妙な結果を産むわけですな。。。 きもちわるいなあ。 ---- ちなみに。 関数への参照渡しに配列の要素を与えた場合だとこの珍現象は起きないのでした。 これは関数の実行が終われば、「箱のなかの箱」に別名を与えたものが解消されるから 「参照型」を用意する必要がないからかなーとか思いました。 >> php > function hoge(&$a){ $a = 123; } php > $b = 24; php > hoge($b); php > var_dump($b); int(123) php > $arr = array(1); php > hoge($arr[0]); php > var_dump($arr); array(1) { [0]=> int(123) } << グローバル変数使えば別名付けられるんじゃないか、と思って試してみましたが そもそも、グローバル変数に参照代入してもグローバル変数には影響与えないみたいです。 こっちもかなり凶悪なハマりポイントですね。。。 >> php > $g = "default"; php > function hoge(){global $g; $a = 1; $g = &$a; $a = 10; } php > hoge(); php > var_dump($g); string(7) "default" << ---- 以上をまとめると、 1.配列の要素の参照代入は特別な目的がない限りしないこと 2.グローバル変数に参照代入はしないこと ってところでしょうかねぇ。 適当に実験して得られた結論なので、PHP詳しい人からのアドバイスがほしいです。
関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ