1. 程式人生 > >深入理解PHP中賦值與引用

深入理解PHP中賦值與引用

str ring int 之前 不同 重新 small nts 計數

【原文】

先看下面的問題:

1 2 3 4 5 6 7 8 <?php $a = 10;//將常量值賦給變量,會為a分配內存空間 $b = $a;//變量賦值給變量,是不是copy了一份副本,b也分配了內存空間呢? $c = &$a;//引用是不會為c分配空間的,c和a是共用一份空間的。 ?>

  對於中間的那個問題,你的答案是什麽呢?在今天之前,我的答案是會為b分配內存空間。因為我是這麽理解的:

  &賦值的時候,視為一個變量定義了一個別名,增加了一個對內存空間的引用。改變其中一個,會影響其他的引用。而使用unset()時,只是斷開了對變量內存空間的引用,內存空間不會釋放。

  而 = 賦值則不同,它會重新開辟一份內存空間存儲原變量的副本。兩者之間的修改不會相互影響。

而下面的程序則印證了這一點:

1 2 3 4 5 6 7 8 9 10 11 12 <?php $a = 10;//將常量值賦給變量,會為a分配內存空間 $b = $a;//變量賦值給變量,是不是copy了一份副本,b也分配了內存空間呢? $c = &$a;//引用是不會為c分配空間的,c和a是共用一份空間的。 $a = 5; echo $c;//輸出5,因為a和c 是指向同一個內存空間 echo PHP_EOL; echo $b;//由於b是副本,對a的操作不會影響b,輸出10
?>

那如果

1 $b = $a;//之後a 和 b 都不做任何改變,保持一致

  有這麽一個問題,如果 = 賦值之後,兩個變量都不曾改變,如果是兩份副本,豈不是太浪費內存?

PHP中實際上避免了這種情況。

  PHP中將一個變量賦值給新變量時,不會立即為新變量分配內存空間,只是增加了對內存空間的引用。當原變量或者新變量作出任何改變時,才會為新變量 分配一塊內存空間。

1 2 3 4 5 6 7 8 9 <?php $a = 1; $b = $a; echo $a; //在此之前,b都是和a共用內存空間的。
$a = 2;//a作出了改變,此時b才會有自己的空間 ?>

  每個php變量存在一個叫"zval"的變量容器中。一個zval變量容器,除了包含變量的類型和值,還包括兩個字節的額外信息。第一個是"is_ref",是個bool值,用來標識這個變量是否是屬於引用集合(referenceset)。通過這個字節,php引擎才能把普通變量和引用變量區分開來,由於php允許用戶通過使用&來使用自定義引用,zval變量容器中還有一個內部引用計數機制,來優化內存使用。第二個額外字節是"refcount",用以表示指向這個zval變量容器的變量(也稱符號即symbol)個數。當"refcount"的值是1時,"is_ref"的值總是FALSE.

  安裝xdebug之後,利用xdebug_debug_zval(),可以看到zval結構:

  如下:

  

1 2 3 4 5 6 7 8 9 10 <?php $a = 1; $b = $a; echo $a; //在此之前,b都是和a共用內存空間的。 xdebug_debug_zval(‘b‘); $a = 2;//a作出了改變,此時b才會有自己的空間 xdebug_debug_zval(‘b‘); ?>

  輸出:

b:

(refcount=2, is_ref=0),

int

1

b:

(refcount=1, is_ref=0),

int

1

由上面的結果可以看到,在a作出改變之前,引用計數是2 ,當a作出改變之後,b的引用計數變為1,是因為b重新分配了空間

深入理解PHP中賦值與引用