引用是什麼
在 PHP 中引用意味著用不同的名字訪問同一個變數內容。這並不像 C 的指標,替代的是,引用是符號表別名。注意在 PHP 中,變數名和變數內容是不一樣的,因此同樣的內容可以有不同的名字。最接近的比喻是 Unix 的檔案名稱和檔案本身——變數名是目錄條目,而變數內容則是檔案本身。引用可以被看作是 Unix 檔案系統中的 hardlink。
引用做什麼
PHP 的引用允許用兩個變數來指向同一個內容。
當 $a =& $b; 時 $a 和 $b 指向了同一個變數。
提示:$a 和 $b 在這裡是完全相同的,這並不是 $a 指向了 $b 或者相反,而是 $a 和 $b 指向了同一個地方。
可以將一個變數通過引用傳遞給函數,這樣該函數就可以修改其參數的值。文法如下:
代碼如下 |
複製代碼 |
<?php function foo(&$var) { $var++; } $a=5; foo($a); echo $a; // 輸出的是:6 |
PHP引用符&
關於php的引用(就是在變數或者函數、對象等前面加上&符號)的作用,我們先看下面這個程式。
代碼如下 |
複製代碼 |
<?php $a = 100; //聲明變數a $b = &$a; //聲明變數b,引用自變數a echo "$a <br />"; echo "$b <br />"; $a++; //變數a自增1 echo "$a <br />"; echo "$b <br />";//查看變數b,也增加了1,說明使用的是同一儲存單元 ?> 程式運行結果: 100 100 101 101 |
很多人誤解php中的引用跟C當中的指標一樣,事實上並非如此,而且很大差別。C語言中的指標除了在數組傳遞過程中不用顯式申明外,其他都需要使用*進行定義,而php中對於地址的指向(類似指標)功能不是由使用者自己來實現的,是由Zend核心實現的,php中引用採用的是“寫時拷貝”的原理,就是除非發生寫操作,指向同一個地址的變數或者對象是不會被拷貝的。
php預設為傳值傳遞:
代碼如下 |
複製代碼 |
<?php $a = 20; $b = $a; $a = $a + 10; echo $a.' and '.$b; ?> |
程式運行結果:
30 and 20
要是想變為地址傳遞需要加&,既:
代碼如下 |
複製代碼 |
<?php $a = 20; $b = &$a; $a = $a + 10; echo $a.' and '.$b; ?> |
程式運行結果:
就是說,&把$a的地址傳給了$b,這樣的話這兩個變數現在共用一個記憶體的儲存地區,就是說它們的值是一樣的。
同樣的文法可以用在函數中,它返回引用,以及用在 new 運算子中:
代碼如下 |
複製代碼 |
view sourceprint? 1 <?php 2 $bar =& new fooclass(); 3 $foo =& find_var($bar); 4 ?> |
引用做的第二件事是用引用傳遞變數。這是通過在函數內建立一個本地變數,並且該變數在呼叫範圍內引用了同一個內容來實現的。說的通俗點就是一個函數的參數是一個本地變數的引用。下面再舉例說明一下:
代碼如下 |
複製代碼 |
<?php function foo(&$val1, $val2) { $val1 += 1; $val2 += 1; } $a=5; $b=10; foo($a,$b); echo $a; echo $b; ?> |
運行這段代碼是給函數傳遞兩個參數,一個是引用$a的內容,一個是$b的值,在執行此函數後,發現$a的內容改變了,而$b的內容則沒有變化。
PHP引用以及誤區
PHP中的引用可以理解成變數的別名。由於PHP的變數名是儲存在符號表(symbol table)中的,變數內容是儲存在堆中,引用就是用符號表中的不同符號(symbol)名稱來訪問同一儲存內容,和Unix檔案系統中的hardlink是同一個概念,比如:
代碼如下 |
複製代碼 |
<?php $a = 1; $b = &$a; //$a與$b指向同一內容 $b = 2; echo $b; //2 echo $a; //2 傳遞引用 引用傳遞很簡單,就是一個「&」符號,比如: <?php function foo(&$a) { $a = 2; } $b = 1; foo($b); echo $b; //2 |
返回引用
大多數情況下並不需要返回引用來提高效能,zend引擎會自己進行最佳化,但是如果你非得返回引用得話,可以按照以下方式來返回引用:
代碼如下 |
複製代碼 |
<?php class foo { public $value = 42; public function &getValue() { // 需要一個"&" return $this->value; } } $obj = new foo; $myValue = &$obj->getValue(); // 還需要一個"&",$myValue是對class foo中的$value的引用 $obj->value = 2; // 修改對象的$value屬性 echo $myValue; // 輸出2,$myValue與class foo中的$value值相同 |
與指標的區別
引用與指標很像,但是其並不是指標,看如下的代碼:
代碼如下 |
複製代碼 |
<?php $a = 0; $b = &a; echo $a; //0 unset($b); echo $a; //0
|
由於$b只是$a的別名,所以即使$b被釋放了,$a沒有任何影響,但是指標可不是這樣的,看如下代碼:
代碼如下 |
複製代碼 |
#include <stdio.h> int main(int argc, char const *argv[]) { int a = 0; int* b = &a; printf("%in", a); //0 free(b); printf("%in", a); //*** error for object 0x7fff6350da08: pointer being freed was not allocated } |
由於b是指向a的指標,所以釋放了b的記憶體之後,再訪問a就會出現錯誤,比較明顯的說明了PHP引用與C指標的區別。
對象與引用
在PHP中使用對象的時候,大家總是被告知“對象是按照引用傳遞的”,其實這是個誤區。PHP的物件變數儲存的是此對象的一個標示符,在傳遞對象的時候,其實傳遞的就是這個標示符,而並不是引用,看如下代碼:
代碼如下 |
複製代碼 |
<?php $a = new A; $b = $a; $b->testA = 2; /* * 此時$a,$b的關係: * +-----------+ +-----------------+ * $a --> | object id | ---> | object(Class A) | * +-----------+ +-----------------+ * ^ * +-----------+ | * $b --> | object id | ---------+ * +-----------+ * * */ $c = new B; $a = $c; $a->testB = "Changed Class B"; /* * 此時$a,$b,$c的關係: * +-----------+ +-----------------+ * $b --> | object id | ---> | object(Class A) | * +-----------+ +-----------------+ * * +------------+ * $a --> | object id2 | -------------+ * +------------+ | * v * +------------+ +-----------------+ * $c --> | object id2 | ---> | object(Class B) | * +------------+ +-----------------+ */ echo "object a: "; var_dump($a); //["testB"]=> string(15) "Changed Class B" echo "object b: "; var_dump($b); //["testA"] => int(2) echo "object c: "; var_dump($c); //["testB"]=> string(15) "Changed Class B" |
如果對象是按照引用傳遞的,那麼$a, $b, $c輸出的內容應該一樣,事實上結果並非如此。 看下面通過引用傳遞對象的列子:
代碼如下 |
複製代碼 |
<?php $aa = new A; $bb = &$aa; // 引用 $bb->testA = 2; /* * 此時$aa, $bb的關係: * * +-----------+ +-----------------+ * $bb --> | object id | ---> | object(Class A) | * +-----------+ +-----------------+ * ^ * | * $aa ---------+ * * */ $cc = new B; $aa = $cc; $aa->testB = "Changed Class B"; /* * 此時$aa, $bb, $cc的關係: * * +-----------+ +-----------------+ * | object id | ---> | object(Class A) | * +-----------+ +-----------------+ * * $bb ---->-----+ * | * $aa ---->-----+ * | * v * +------------+ * | object id2 | --------------+ * +------------+ | * v * +------------+ +-----------------+ * $cc --> | object id2 | ---> | object(Class B) | * +------------+ +-----------------+ */ echo "object aa: "; var_dump($aa); //["testB"]=>string(15) "Changed Class B" echo "object bb: "; var_dump($bb); //["testB"]=>string(15) "Changed Class B" echo "object cc: "; var_dump($cc); //["testB"]=>string(15) "Changed Class B" |
此時$aa,$bb,$cc三者內容完全一樣,所以可以看出對象並不是按照引用傳遞,要儘快走出這個誤區。