PHP 引用的概念
在 PHP 中引用意味著用不同的名字訪問同一個(gè)變量?jī)?nèi)容。它不是C的指針,保存的并不是內(nèi)存地址,無法進(jìn)行指針運(yùn)算。引用只是符號(hào)表的別名。就像 Unix 系統(tǒng)中的硬鏈接, Windows 系統(tǒng)中的快捷方式。
上面是官方手冊(cè)中的原文,怎么說呢,引用其實(shí)和我們印象中的C里面的指針并不是相同的概念。指針是針對(duì)真實(shí)內(nèi)存的操作,引用是針對(duì)指向這個(gè)內(nèi)存的符號(hào)表的操作。還是從操作系統(tǒng)的快捷方式來說,快捷方式是可以刪的,這就是PHP的引用。而C不僅刪了快捷方式,還把原文件也給刪了,這就是C的指針操作。
// 引用不是指針$a = 1;$b = &$a;echo $a, ’===’, $b, PHP_EOL;unset($b);echo $a, ’===’, $b, PHP_EOL;
上面的代碼是在PHP中,我們把b變量指向b變量指向a,作為a的引用變量。然后刪除a的引用變量。然后刪除b,對(duì)$a沒有任何影響。
#include <stdio.h>#include <stdlib.h>int main(){ // C 中的指針和引用 int a = 1; int* b = &a; printf('%in', a); // 1 free(b); // free b printf('%in', a); //get error: *** error for object 0x7fff6350da08: pointer being freed was not allocated return 0;}
而C中的引用指針就不行了,我們把b變量刪掉后,再打印a變量就直接報(bào)錯(cuò)了。
雖然說PHP的底層也是C寫得,但我們都知道C中的指針是出了名的變態(tài),沒有一定的功底非常容易出錯(cuò)。所以PHP的開發(fā)者沒有暴露C的原始指針能力,而是采用了和Java之類的類似的引用能力。這也是現(xiàn)代語(yǔ)言的特性,不需要我們過多的關(guān)注過于底層的能力,而將更多的時(shí)間放在業(yè)務(wù)實(shí)現(xiàn)上。
引用在數(shù)組和對(duì)象中的使用如果具有引用的數(shù)組被拷貝,其值不會(huì)解除引用。對(duì)于數(shù)組傳值給函數(shù)也是如此。
$arr1 = ['a', 'b'];$t1 = &$arr1[1];$arr2 = $arr1;$arr2[1] = 'c';var_dump($arr1);// array(2) {// [0]=>// string(1) 'a'// [1]=>// &string(1) 'c'// }$arr1 = ['a', 'b'];$t1 = &$arr1[1];unset($t1); // unset 掉引用$arr2 = $arr1;$arr2[1] = 'c';var_dump($arr1);// array(2) {// [0]=>// string(1) 'a'// [1]=>// string(1) 'b'// }
這個(gè)其實(shí)挺有意思的,我們對(duì)比這兩個(gè)例子可以看出一個(gè)問題,t變量指向t變量指向arr[1]的引用。arr2直接=這個(gè)arr2直接=這個(gè)arr1,沒有使用引用,然后arr2修改了arr2修改了arr2[1]的內(nèi)容,arr1相應(yīng)的內(nèi)容也發(fā)生了改變,如果unset掉arr1相應(yīng)的內(nèi)容也發(fā)生了改變,如果unset掉t變量,則$arr1相應(yīng)的內(nèi)容就不會(huì)發(fā)生改變。對(duì)此,我在文檔中找到了下面的解釋:
由于PHP內(nèi)部工作的特殊性,如果對(duì)數(shù)組的單個(gè)元素進(jìn)行引用,然后復(fù)制數(shù)組,無論是通過賦值還是通過函數(shù)調(diào)用中的值傳遞,都會(huì)將引用復(fù)制為數(shù)組的一部分。這意味著對(duì)任一數(shù)組中任何此類元素的更改都將在另一個(gè)數(shù)組(和其他引用中)中重復(fù),即使數(shù)組具有不同的作用域(例如,一個(gè)是函數(shù)內(nèi)部的參數(shù),另一個(gè)是全局的)!在復(fù)制時(shí)沒有引用的元素,以及在復(fù)制數(shù)組后分配給其他元素的引用,將正常工作(即獨(dú)立于其他數(shù)組)。
不僅僅是數(shù)組,對(duì)象的引用也會(huì)有一些好玩的問題。
$o1 = new stdClass();$o1->a = ’a’;var_dump($o1);// object(stdClass)#1 (1) {// ['a']=>// string(1) 'a'// }$o2 = &$o1;$o3 = $o1;$o2->a = ’aa’;var_dump($o1);// object(stdClass)#1 (1) {// ['a']=>// string(2) 'aa'// }var_dump($o3); // $o2修改了$a為’aa’,$o3也變成了’aa’// object(stdClass)#1 (1) {// ['a']=>// string(2) 'aa'// }$o1->a = ’aaa’;$o1 = null;var_dump($o2); // $o2引用變成了null// NULLvar_dump($o3); // $o3不僅引用還存在,并且$a變成了’aaa’// object(stdClass)#1 (1) {// ['a']=>// string(3) 'aaa'// }
上面例子中有三個(gè)對(duì)象,o1、o1、o2、o3,其中,o3,其中,o2是對(duì)o1的引用,o1的引用,o3是直接賦值為o1。對(duì)o1。對(duì)o2屬性的操作不僅會(huì)反映在o1中,也會(huì)反映到o1中,也會(huì)反映到o3中。其實(shí)我們之前專門有一篇文章就講的這個(gè)問題,首先對(duì)象默認(rèn)賦值就是引用,其次這個(gè)例子很好地證明了引用就是一個(gè)符號(hào)表的綁定。刪除了快捷方式對(duì)原始對(duì)象和其他快捷方式?jīng)]有任何影響。
引用的傳遞關(guān)于引用在方法參數(shù)上的傳遞,最重要的是記住兩點(diǎn):一是方法內(nèi)部修改了變量外部也會(huì)變,這是引用的特性嘛;二是只能傳遞變量、New 語(yǔ)句、從函數(shù)中返回的引用三種類型。
error_reporting(E_ALL);function foo(&$var){ $var++; echo ’foo:’, $var;}function bar() // Note the missing &{ $a = 5; return $a;}foo(bar()); // 自 PHP 5.0.5 起導(dǎo)致致命錯(cuò)誤,自 PHP 5.1.1 起導(dǎo)致嚴(yán)格模式錯(cuò)誤 // 自 PHP 7.0 起導(dǎo)致 notice 信息,Notice: Only variables should be passed by referencefoo($a = 5); // 表達(dá)式,不是變量, Notice: Only variables should be passed by reference// foo(5); // 導(dǎo)致致命錯(cuò)誤 !5是個(gè)常量!///////////////////////////////// 正確的傳遞類型$a = 5;foo($a); // 變量function &baz(){ $a = 5; return $a;}foo(baz()); // 從函數(shù)中返回的引用function foo1(&$var){ print_r($var);}foo1(new stdClass()); // new 表達(dá)式引用的返回
引用的返回并不是經(jīng)常使用的一個(gè)能力。文檔中的原文是:不要用返回引用來增加性能,引擎足夠聰明來自己進(jìn)行優(yōu)化。僅在有合理的技術(shù)原因時(shí)才返回引用!
$a = 1;function &test(){ global $a; return $a;}$b = &test($a);$b = 2;echo $a, PHP_EOL;
當(dāng)你想要返回一個(gè)引用變量的時(shí)候,一定要給方法定義和方法調(diào)用的時(shí)候都使用&符號(hào)。這個(gè)是需要注意的點(diǎn)。當(dāng)其他地方修改原本的變量值或者返回的變量值經(jīng)過修改后,都會(huì)影響到所有調(diào)用這個(gè)值的地方。所以說,引用的返回是比較危險(xiǎn)的,因?yàn)槟悴磺宄裁磿r(shí)候在什么地方這個(gè)值可能發(fā)生了修改,對(duì)于bug的排查會(huì)非常困難。
引用的取消取消引用其實(shí)就是直接unset掉變量就可以了。但是一定要記住,PHP中的引用是指向的符號(hào)表,對(duì)原始真實(shí)的值是不起作用的,所以即使unset掉了最原始的那個(gè)變量,對(duì)其它引用賦值的變量也不會(huì)有影響!!
$a = 1;$b = &$a;$c = &$b;$b = 2;echo ’定義引用后:’, $a, ’===’, $b, ’===’, $c, PHP_EOL;unset($b);$b = 3;echo ’取消$b的引用,不影響$a、$c:’, $a, ’===’, $b, ’===’, $c, PHP_EOL;$b = &$a;unset($a);echo ’取消$a,不影響$b、$c:’, $a, ’===’, $b, ’===’, $c, PHP_EOL;// 定義引用后:2===2===2// 取消$b的引用:2===3===2// 取消$a,不影響$c:===3===2$a = 1;$b = & $a;$c = & $b; // $a, $b, $c reference the same content ’1’$a = NULL; // All variables $a, $b or $c are unsetecho ’所有引用成空:’, $a, ’===’, $b, ’===’, $c, PHP_EOL;總結(jié)
這一次算是比較徹底的把引用說了個(gè)透。關(guān)于PHP的引用只要記住了它的定義就非常好理解了,最直觀的就是當(dāng)成是操作系統(tǒng)的快捷方式就好了,并沒有我們想象中的那么難,和C的指針相比真的只是娃娃級(jí)別,多多練習(xí)多多復(fù)習(xí)自然就能很好地掌握使用啦!
測(cè)試代碼: github.com/zhangyue050…
以上就是PHP的中引用的概念的詳細(xì)內(nèi)容,更多關(guān)于PHP 引用的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. XML解析錯(cuò)誤:未組織好 的解決辦法2. IE6/IE7/IE8/IE9中tbody的innerHTML不能賦值的完美解決方案3. js的一些潛在規(guī)則使用分析4. Properties 持久的屬性集的實(shí)例詳解5. asp批量添加修改刪除操作示例代碼6. PHP字符串前后字符或空格刪除方法介紹7. 使用Spry輕松將XML數(shù)據(jù)顯示到HTML頁(yè)的方法8. ASP腳本組件實(shí)現(xiàn)服務(wù)器重啟9. 三個(gè)不常見的 HTML5 實(shí)用新特性簡(jiǎn)介10. PHP循環(huán)與分支知識(shí)點(diǎn)梳理
