phpの参照引数


実態の代入時では、$a,$bは同じhogeを参照しています。ただし、$aを更新するとき、新規にhogeを割り当て、refcount=1として(この時、$bのrefcountも1になります。)更新(Copy On Write)します。

なお、参照カウンターは変数に属するカウンターとして見るより、'hoge'という実態に付属する参照されるカウンターとして理解するほうがいいかと思います。
<?php
       $a = 'hoge';
       $b = $a;
       xdebug_debug_zval('a');
       xdebug_debug_zval('b');
?>

[root@localhost test]# php c.php
a: (refcount=2, is_ref=0)='hoge'
b: (refcount=2, is_ref=0)='hoge'
参照の代入時では、$a,$bは同じhogeを参照(refcount=2)しています。$a = 'hoge'ではis_ref=0ですが、=&する事は、$bに参照として$aを設定するでけでなく、$aをも参照型にすることでもあります。
<?php
       $a = 'hoge';
       $b = &$a;
       xdebug_debug_zval('a');
       xdebug_debug_zval('b');
?>

[root@localhost test]# php c.php
a: (refcount=2, is_ref=1)='hoge'
b: (refcount=2, is_ref=1)='hoge'

本題

で、関数引数のそれを見てみたいと思います。関数引数の取り扱い実装は、内部的にテンポラリ変数をアロケートし(Cでのスタック領域みたいのもの。でないと再帰処理ができない。)、コール先関数で、テンポラリ変数を引数変数に設定している。という解釈です。この前提でみてみると、つじつまが合うようなので・・・。


参照引数とする関数で、参照変数でコールした場合です。

参照変数としてtest($a)がコールされ、test()の宣言は参照引数のため、テンポラリ変数に参照変数のまま$aを設定します(この時refcount=3)。次にテンポラリ参照変数を、参照変数$cに設定することです。従って'hoge'を参照している変数は4となり、$cの参照カウンタは4となります。
<?php

       $a = 'hoge';
       $b = &$a;
       xdebug_debug_zval('a');
       print "\n";

       test($a);

       xdebug_debug_zval('a');

function        test(&$c)
{
       xdebug_debug_zval('c');
       print "\n";
}
?>

[root@localhost test]# php a.php
a: (refcount=2, is_ref=1)='hoge'

c: (refcount=4, is_ref=1)='hoge'

a: (refcount=2, is_ref=1)='hoge'
参照引数とする関数で、実態変数でコールした場合です。

test($a)で参照型のテンポラリ変数に$aを設定する場合、$aは実態型変数ですので、新規に'hoge'を割り当て、$aはその割り当てた'hoge'にリンクします。この時点でrefcount=1($bもrefcount=1となっているはずです。test()からコールした後の$aはrefcount=1となっています。)となります。それをテンポラリ変数、テンポラリ変数を$cへとセットするため、$cの参照カウンタは3になっています。
<?php

       $a = 'hoge';
       $b = $a;
       xdebug_debug_zval('a');
       print "\n";

       test($a);

       xdebug_debug_zval('a');

function        test(&$c)
{
       xdebug_debug_zval('c');
       print "\n";
}
?>

[root@localhost test]# php a.php
a: (refcount=2, is_ref=0)='hoge'

c: (refcount=3, is_ref=1)='hoge'

a: (refcount=1, is_ref=0)='hoge'
実態引数とする関数で、実態変数でコールした場合です。

Copy On Writeにより、すべての変数は実態型ゆえ、同じ'hoge'をリンクしておけばいいわけです。
<?php

       $a = 'hoge';
       $b = $a;
       xdebug_debug_zval('a');
       print "\n";

       test($a);

       xdebug_debug_zval('a');

function        test($c)
{
       xdebug_debug_zval('c');
       print "\n";
}
?>

[root@localhost test]# php a.php
a: (refcount=2, is_ref=0)='hoge'

c: (refcount=4, is_ref=0)='hoge'

a: (refcount=2, is_ref=0)='hoge'
実態引数とする関数で、参照変数でコールした場合です。

$aは参照型で、しかも参照カウンターは$bとの絡みで、2となっているため、テンプ変数に$aを設定する場合、test()の宣言は実態ゆえ、新規に'hoge'を割り当て、実態型テンプ変数はそこにリンクします。それを実態型変数$cに設定するため、refcount=2となっているわけです。この場合、COWとするのでなく、関数内で更新される前提で処理されるようです。
<?php

       $a = 'hoge';
       $b = &$a;
       xdebug_debug_zval('a');
       print "\n";

       test($a);

       xdebug_debug_zval('a');

function        test($c)
{
       xdebug_debug_zval('c');
       print "\n";
}
?>

[root@localhost test]# php a.php
a: (refcount=2, is_ref=1)='hoge'

c: (refcount=2, is_ref=0)='hoge'

a: (refcount=2, is_ref=1)='hoge'

追記

Cが主たる開発言語として歩んできたため、当初phpの参照の取り扱いには戸惑ったものです。思えば、&はCではコンパイラの仕事ですが、phpではそのような区切りがないわけで、宣言というよりコマンドともいえるわけです。で、コンパイラ型の言語と、スクリプトとようなインタープリタ型言語の違い故という事で、溜飲が下がった思いです。なお、内容は実際検証したものでありません。

最終更新 2013/09/08 16:20:55 - north
(2013/09/08 16:05:49 作成)


検索

アクセス数
3586279
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。