在記憶中 PHP 簡單變量的拷貝是按值傳遞,數組和對象的拷貝是按引用傳遞,即通過引用來實現。
簡單變量和對象好理解:
<?php // 簡單變量的拷貝 $a = 'human'; $b = $a; $b = 'cat'; var_dump($a); // string 'human' (length=5) // 對象的拷貝 class A{} $a = new A(); $b = $a; $b->name = 'jack'; var_dump($a); // object(A)[1] public 'name' => string 'jack' (length=4)
對象 $a 和 對象 $b 的內存地址指向了同一個地方,即針對 $a 和 $b 所做的操作都針對同一個實例。
PHP 中可以通過 clone 關鍵字按值拷貝對象:
<?php class A{ public $name = 'Vardy'; }
$a = new A(); $b = clone $a; $b->name = 'dee'; var_dump($a); // object(A)[1] public 'name' => string 'Vardy' (length=5) var_dump($b); // object(A)[2] public 'name' => string 'dee' (length=3)
數組:
<?php // 數組的拷貝 $a = ['human', 'orc']; $b = $a; $b[0] = 'elve'; var_dump($a); // array (size=2) // 0 => string 'human' (length=5) // 1 => string 'orc' (length=3)
這樣看,數組的拷貝是按值傳遞的.
另一個例子 數組的迭代器:
<?php $a = ['human', 'orc', 'elven', 'undead']; next($a); $b = $a; // 此時指針位置也隨着數組的復制而復制 var_dump(current($a)); // string 'orc' (length=3) var_dump(current($b)); // string 'orc' (length=3)
當數組拷貝時,數組的指針位置也隨着復制。
(next:http://php.net/manual/zh/function.next.php
current:http://php.net/manual/zh/function.current.php)
<?php $a = ['human', 'orc', 'elven', 'undead']; end($a); next($a); // 數組指針非法,返回 FALSE $b = $a; var_dump(current($a)); // string 'human' (length=5) var_dump(current($b)); // boolean false
當數組在拷貝前,指針的位置是非法時,拷貝后分別打印兩個數組指針所指向的當前單元時,出現了不一樣的情況。
原因是數組變量發生拷貝后,兩個變量中先發生寫操作的,其指針被初始化,即指向第一個元素,而 current 方法也會產生寫操作,因此 $a 的數組指針當前指向的是 'human',而 $b current 方法則返回 FALSE。
以下代碼同理, $b 首先發生了寫操作:
<?php $a = ['human', 'orc', 'elven', 'undead']; end($a); next($a); $b = $a; $b[] = 'dwarf'; var_dump(current($a)); // boolean false var_dump(current($b)); // string 'human' (length=5)
解析:數組的拷貝是值傳遞。PHP 在管理內存方面有一個機制叫寫時復制(COW,Copy On Write),保證了變量間復制值不浪費內存:當一個變量的值復制到另一個變量時,PHP 沒有為復制值使用更多的內存,相反,它會更新符號表來說明兩個變量擁有相同的內存塊,所以當執行下面的代碼時並沒有創建一個新的數組:
<?php $a = ['human', 'orc', 'elven', 'undead']; $b = $a;
當修改了 $a 或 $b 任意一個副本時,PHP 將分配所需的內存來進行復制:
$b[] = 'dwarf';
參考:
Programming PHP 3rd Edition