ようこそゲストさん

CPA-LABテクニカル

2008/01/27(日) 変数管理の基礎(PHPの参照とは何か)-PHP変数管理 (2)

内部構造がわからず、もやもやとした気分でコーディングするのは、あまり気分が良くない。
PHPの変数管理の詳細を知ると、複雑な参照代入や配列などの使い方の幅が増えてくる。はず、ということで、いろいろ調べてみた。変数や関数の受け渡しについては知っているつもりで、間違った知識で書いていることも多いことがわかり、自分でもあぜんとした。ので、いくつかわかった(つもり)のことを書いてみる。

PHP変数管理の目次はこちら 

変数管理はワンクッションあり→シンボルテーブル

PHPで、$a='one';と書いた場合、内部的に 変数名a → 値one という単純な表が作られるわけではない。「変数名aは、値の格納庫コンテナ(zval構造体)No.#0001と対応している」というシンボルテーブルが作られ、格納庫(zval)No.#0001にoneという値が書き込まれる。
code1
$a='one';

figure1  $a='one'; で作成されるPHP内部の変数管理表
シンボルテーブル
変数名格納庫(zval)No.
a#0001

格納庫(zval)
No.valuetype
#0001onestring(文字列型)
格納庫zvalについては別ページで改めて説明する。

参照代入とは  -シンボルテーブルの多重割り当て


シンボルテーブルには、同じ格納庫(zval)No.に、複数の変数名が割り当てられることがある。それが参照代入と呼ばれる。
例えば、以下のコードは、同じ格納庫(zval)No.にa,bの変数名が割り当てられる。
code2
$a= <span style="color:red;font-weight:bold;">&</span> $b; 
figure2  参照代入された後
シンボルテーブル
変数名格納庫(zval)No.
a#0001
b#0001

格納庫(zval)
No.type
#0001onestring
このような構造だからこそ、
code3
$a='one';
$b= & $a;
$b= 'two';
としたとき、同じ格納庫(zval)No.に書き込まれるわけだから、$a,$bともに two となる。
ここで実体と呼ばれる、スカラー値が入った格納庫zvalは、決してコピーされていないことに注意する。実体はただ1つ。上の例では、その1つの実体をを呼ぶのに$aとも$bとも使われるわけだ。

C言語のポインタとの違い

ここが、C言語のポインタと違うと言われる理由である。C言語のポインタと同じものでであれば、以下のようになっているはずである。

figure3
シンボルテーブル
変数名格納庫(zval)No.
a#0001
b#0002

格納庫(zval)
No.type
#0001onestring
#0002#0001(long?)
※もし、PHPの参照代入がC言語のポインタと同じだったらの例示

この違いを理解しておかないと、後々、いろんなところで、「はまる」ことになる。

$aと$bは等価である。($a =& $b)  -ただし、$a,$bがオブジェクトの時は除く

以下のコードは同じ結果となる。$aと$bは等価である。
code4
$a='one';
$b= & $a;
$a= 'two';

code5
$a='one';
$b= & $a;
$b= 'two';

結果は以下のとおり。

figure4
シンボルテーブル
変数名格納庫(zval)No.
a#0001
b#0001

格納庫(zval)
No.type
#0001twostring

誤解されがちなのは、=&演算子が、参照「代入」と翻訳されていることから、$b=& $a;としたときに、まるで、$bに何かの値が書き込まれるような感覚になることである。それはCのポインタであり、PHPの参照とは違うことをよく理解する必要がある。あくまでも、PHPの参照代入は、結びつけるべき左辺の変数名をシンボルテーブルに追加することである。シンボルテーブルを利用して、変数名と格納庫(zval)Noを新たに連動させる、ということである。
誤解を恐れずにいえば、$b=& $a;の場合、$bの実体(zval格納庫)はないのだ。もう少し正確にいえば、$aの実体を間借りするようなものだ。この間借りすることをもって、エイリアスという言い方をする人がいるようだ。もっと正確にいえば、$aと$bは等価なので、$aの実体を$bの実体としても扱う、ということになる。


ただし、上記は、オブジェクトの時だけは異なった結論になる。それは、のちほど説明したい。

unset と =null の違い

では、ここでunset($a)としたら、どうなるか。
$a=null;とどう違うか、検証する。

まず、$a=nullであれば、格納庫(zval)No.#0001にnull値が書き込まれるわけだから、$a,$bともにnullとなる。$a,$bは連動しているのだ。

ではunsetはどうだろうか。
code6
unset($a);
unsetは、「変数を破棄する」でない。「シンボルテーブルからその変数名を削除する」という動作をする。つまり、
figure5
シンボルテーブル
変数名格納庫(zval)No.
a(削除)#0001(削除)
b#0001

格納庫(zval)
No.type
#0001twostring


となるので、$aはなくなってしまうが、$bの値はtwoのままである。

つまり
code7
$a='one';
$b= & $a;
$a= 'two';
unset ($a);
とすると、$bの値は、 two となる。

Ohhh!まるで、$bが$aをヤドカリのように乗っ取ってしまったようだ。

まとめ

このようにPHPでは、変数の名前とその値は切り離されており、シンボルテーブルがそれを結びつける働きをしている。シンボルテーブルでは、複数の変数名に対して、同じ実際の値(実体:zval構造体に収納)を、対応(reference or asign)させるさせることができるため、柔軟な変数運用をすることができる。

figure6
変数名($a)→シンボルテーブル→実際の値(実体:zval構造体に収納)
変数名($b)
変数名(・・・)


次回は、$a=$b;について説明したい。単純であるが、奥が深いcodeである。


PHP変数管理の目次はこちら 

1: FCC 2011年11月28日(月) 午前10時27分

おー。それしらんかっとんてんちんとんしゃん


名前:  非公開コメント   

  • TB-URL  http://www.cpa-lab.com/tech/029/tb/
  • 変数の代入は、値のコピーにあらず CPA-LABテクニカル spok
    前回の変数管理の基礎(PHPの参照とは何か) でシンボルテーブルについて説明した。これがわかっていないとPHPの変数管理はわからない。今回は、もっとも基本的な変数の値の代入、について説明する。code1$a=$bなぜ、こんな単純なことをいちいち説明しなけれ...