本篇文章給大家帶來的內容是關於php變數的引用賦值與傳值賦值的詳細介紹(代碼),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所協助。
一、使用 memory_get_usage() 查看PHP記憶體使用量量
1. 傳值賦值
// 定義一個變數$a = range(0, 10000);var_dump(memory_get_usage());// 定義變數b,將a變數的值賦值給b$b = $a;var_dump(memory_get_usage());// 對a進行修改// COW: Copy-On-Write$a = range(0, 10000);var_dump(memory_get_usage());
輸出結果:
int(989768)int(989856)int(1855608)
定義一個變數 $a = range(0, 10000);
$b = $a;
對a進行修改 $a = range(0, 10000);
PHP寫時複製機制(Copy-on-Write,也縮寫為COW)
顧名思義,就是在寫入時才真正複製一份記憶體進行修改。
COW最早應用在Unix系統中對線程與記憶體使用量的最佳化,後面廣泛的被使用在各種程式設計語言中,如C++的STL等。
在PHP核心中,COW也是主要的記憶體最佳化手段。
在通過變數賦值的方式賦值給變數時,不會申請新記憶體來存放新變數的值,而是簡單的通過一個計數器來共用記憶體。只有在其中的一個引用指向變數的值發生變化時,才申請新空間來儲存值內容,以減少對記憶體的佔用。
在很多情境下PHP都使用COW進行記憶體的最佳化。比如:變數的多次賦值、函數參數傳遞,並在函數體內修改實參等。
2. 引用賦值
// 定義一個變數$a = range(0, 10000);var_dump(memory_get_usage());// 定義變數b,將a變數的引用賦給b$b = &$a;var_dump(memory_get_usage());// 對a進行修改$a = range(0, 10000);var_dump(memory_get_usage());
輸出結果:
int(989760)int(989848)int(989840)
定義一個變數 $a = range(0, 10000);
定義變數b,將a變數的引用賦給b $b = &$a;
對a進行修改 $a = range(0, 10000);
二、使用 xdebug_debug_zval()
查看變數的引用情況
xdebug_debug_zval()
用於顯示變數的資訊。需要安裝xdebug擴充。
1. 傳值賦值
$a = 1;xdebug_debug_zval('a');// 定義變數b,把a的值賦值給b$b = $a;xdebug_debug_zval('a');xdebug_debug_zval('b');// a進行寫操作$a = 2;xdebug_debug_zval('a');xdebug_debug_zval('b');
輸出結果:
a: (refcount=1, is_ref=0)=1a: (refcount=2, is_ref=0)=1b: (refcount=2, is_ref=0)=1a: (refcount=1, is_ref=0)=2b: (refcount=1, is_ref=0)=1
定義變數 $a = 1;
$a = 1;xdebug_debug_zval('a');
輸出
a: (refcount=1, is_ref=0)=1
refcount=1
表示該變數指向的記憶體位址的引用個數變為1
is_ref=0
表示該變數不是引用
定義變數 $b
,把 $a
的值賦給 $b
, $b = $a;
$b = $a;xdebug_debug_zval('a');xdebug_debug_zval('b');
輸出
a: (refcount=2, is_ref=0)=1b: (refcount=2, is_ref=0)=1
refcount=2
表示該變數指向的記憶體位址的引用個數變為2
is_ref=0
表示該變數不是引用
對變數 $a
進行寫操作 $a = 2;
$a = 2;xdebug_debug_zval('a');xdebug_debug_zval('b');
輸出
a: (refcount=1, is_ref=0)=2b: (refcount=1, is_ref=0)=1
因為COW機制,對變數 $a
進行寫操作時,會為變數 $a
新分配一塊記憶體空間,用於儲存變數 $a
的值。
此時 $a
和 $b
指向的記憶體位址的引用個數都變為1。
2. 引用賦值
$a = 1;xdebug_debug_zval('a');// 定義變數b,把a的引用賦給b$b = &$a;xdebug_debug_zval('a');xdebug_debug_zval('b');// a進行寫操作$a = 2;xdebug_debug_zval('a');xdebug_debug_zval('b');
a: (refcount=1, is_ref=0)=1a: (refcount=2, is_ref=1)=1b: (refcount=2, is_ref=1)=1a: (refcount=2, is_ref=1)=2b: (refcount=2, is_ref=1)=2
定義變數 $a = 1;
$a = 1;xdebug_debug_zval('a');
輸出
a: (refcount=1, is_ref=0)=1
refcount=1
表示該變數指向的記憶體位址的引用個數變為1
is_ref=0
表示該變數不是引用
定義變數 $b
,把 $a
的引用賦給 $b
, $b = &$a;
$b = &$a;xdebug_debug_zval('a');xdebug_debug_zval('b');
輸出
a: (refcount=2, is_ref=1)=1b: (refcount=2, is_ref=1)=1
refcount=2
表示該變數指向的記憶體位址的引用個數變為2
is_ref=1
表示該變數是引用
對變數 $a
進行寫操作 $a = 2;
$a = 2;xdebug_debug_zval('a');xdebug_debug_zval('b');
輸出
a: (refcount=2, is_ref=1)=2b: (refcount=2, is_ref=1)=2
因為變數 $a
和變數 $b
指向相同的記憶體位址,其實引用。
對變數 $a
進行寫操作時,會直接修改指向的記憶體空間的值,因此變數 $b
的值會跟著一起改變。
三、當變數時引用時,unset()只會取值 (Dereference),不會銷毀記憶體空間
$a = 1;$b = &$a;// unset 只會取值 (Dereference),不會銷毀記憶體空間unset($b);echo $a;
輸出
定義變數 $a
,並將 $a
的引用賦給變數 $b
銷毀 $b
輸出 $a
雖然銷毀的 $b
,但是 $a
的引用和記憶體空間依舊存在。
輸出
四、php中對象本身就是引用賦值
class Person{ public $age = 1;}$p1 = new Person;xdebug_debug_zval('p1');$p2 = $p1;xdebug_debug_zval('p1');xdebug_debug_zval('p2');$p2->age = 2;xdebug_debug_zval('p1');xdebug_debug_zval('p2');
p1: (refcount=1, is_ref=0)=class Person { public $age = (refcount=2, is_ref=0)=1 }p1: (refcount=2, is_ref=0)=class Person { public $age = (refcount=2, is_ref=0)=1 }p2: (refcount=2, is_ref=0)=class Person { public $age = (refcount=2, is_ref=0)=1 }p1: (refcount=2, is_ref=0)=class Person { public $age = (refcount=1, is_ref=0)=2 }p2: (refcount=2, is_ref=0)=class Person { public $age = (refcount=1, is_ref=0)=2 }
執行個體化對象 $p1 = new Person;
$p1 = new Person;xdebug_debug_zval('p1');
輸出
p1: (refcount=1, is_ref=0)=class Person { public $age = (refcount=2, is_ref=0)=1 }
refcount=1
表示該變數指向的記憶體位址的引用個數變為1
is_ref=0
表示該變數不是引用
把 $p1
賦給 $p2
$p2 = $p1;xdebug_debug_zval('p1');xdebug_debug_zval('p2');
輸出
p1: (refcount=2, is_ref=0)=class Person { public $age = (refcount=2, is_ref=0)=1 }p2: (refcount=2, is_ref=0)=class Person { public $age = (refcount=2, is_ref=0)=1 }
refcount=2
表示該變數指向的記憶體位址的引用個數變為2
對 $p2
中的屬性 age
進行寫操作
$p2->age = 2;xdebug_debug_zval('p1');xdebug_debug_zval('p2');
輸出
p1: (refcount=2, is_ref=0)=class Person { public $age = (refcount=1, is_ref=0)=2 }p2: (refcount=2, is_ref=0)=class Person { public $age = (refcount=1, is_ref=0)=2 }
因為php中對象本身就是引用賦值。對 $p2
中的屬性 age
進行寫操作時,會直接修改指向的記憶體空間的值,因此變數 $p1
的 age
屬性的值會跟著一起改變。
五、實戰例題分析
/** * 寫出如下程式的輸出結果 * * $d = ['a', 'b', 'c']; * * foreach($d as $k => $v) * { * $v = &$d[$k]; * } * * 程式運行時,每一次迴圈結束後變數 $d 的值是什嗎?請解釋。 * 程式執行完成後,變數 $d 的值是什嗎?請解釋。 */
1. 第一次迴圈
推算出進入 foreach
時 $v
、$d[$k]
的值
$k = 0$v = 'a'$d[$k] = $d[0] = 'a'
此時,$v
和 $d[0]
在記憶體中分別開闢了一塊空間
![$v 和 $d[0] 在記憶體中分別開闢了一塊空間](http://md.ws65535.top/xsj/201...
$v = &$d[0]
改變了 $v 指向的記憶體位址
![$v = &$d[0] 改變了 $val 指向的記憶體位址](http://md.ws65535.top/xsj/201...
第一次迴圈後 $d 的值:
2. 第二次迴圈
進入 foreach
時 $v
被賦值為 'b',此時$v
指向的記憶體位址與 $d[0]
相同,且為引用,因此 $d[0]
的值被修改為 'b'
$v = 'b'
=> $d[0] = 'b'
![$v = ‘b’ => $d[0] = ‘b’](http://md.ws65535.top/xsj/201...
推算出進入 foreach
時 $d[$k]
的值
$k = 1$d[$k] = $d[1] = 'b'
![$d[2] = ‘b’](http://md.ws65535.top/xsj/201...
$v = &$d[1]
改變了 $v 指向的記憶體位址
![$v = &$d[1]](http://md.ws65535.top/xsj/201...
第二次迴圈後 $d
的值
3. 第三次迴圈
進入 foreach
時 $v
被賦值為 'c',此時$v
指向的記憶體位址與 $d[1]
相同,且為引用,因此 $d[1]
的值被修改為 'c'
$v = 'c'
=> $d[1] = 'c'
![$v = ‘c’ => $d[1] = ‘c’](http://md.ws65535.top/xsj/201...
推算出進入 foreach
時 $d[$k]
的值
![$d[2] = ‘c’](http://md.ws65535.top/xsj/201...
$v = &$d[2]
改變了 $v 指向的記憶體位址
![$v = &$d[2]](http://md.ws65535.top/xsj/201...
第三次迴圈後 $d
的值
4. 實測
$d = ['a', 'b', 'c'];foreach ($d as $k=>$v){ $v = &$d[$k]; print_r($d);}print_r($d);
輸出:
Array( [0] => a [1] => b [2] => c)Array( [0] => b [1] => b [2] => c)Array( [0] => b [1] => c [2] => c)Array( [0] => b [1] => c [2] => c)