關於這個概念一般都會在PHP的第一堂課說變量的時候給介紹,並且我以前還給其他PHPer介紹這個概念。但是作為一個工作一段時間的PHPer的我,竟然在面試的時候一下子拿不定主意最后還答錯了,很覺得丟臉(下面我會一一道來),所以就寫一篇關於這個概念的博文,該概念在PHP面試中也算是常考的題吧;
首先,要理解變量名存儲在內存棧中,它是指向堆中具體內存的地址,通過變量名查找堆中的內存;
普通傳值,傳值以后,是不同的地址名稱,指向不同的內存實體;
引用傳值,傳引用后,是不同的地址名稱,但都指向同一個內存實體;改變其中一個,另外一個就也被改變;
以下我將通過三個列子來詳細講解這兩個傳值的區別:
Example1:
<?php
//普通傳值
$param1=1;
$param2=2;
$param2 = $param1;
$param1 = 5; //變量1和變量2是兩塊內存,互不影響;
echo $param2; //所以此處還是顯示為1
//引用傳值 ↓↓
$param1=1;
$param2=2;
$param2 = &$param1; //把變量1的內存地址賦給變量2;此時的變量2和變量1全等;
echo $param2;// 1
$param1 = 5; //變量1和變量2是一處內存,改變其中一個,另外一個也被改變;
echo $param2; //顯示為5
?>
Example2:
<?php
//函數中的普通傳值 ↓↓
$param1 = 1;
function add($param2){
$param2=3;
}
$param3=add($param1); //調用方法add,並將變量1傳給變量2,此處是普通傳值,所以變量1和變量2是兩處內存,互不影響;
echo '<br>$param1=='.$param1.'<br>'; //顯示為$param1==1
echo '<br>$param2=='.$param2.'<!-- <br> -->'; //顯示為$param2== 因為$param2是局部變量,函數運行完了以后就自動銷毀,其不能影響全局
//函數中的引用傳值 ↓↓ 注意,php不建議這樣使用,並且php.in里面設置其會報錯;
$param1 = 1;
function add($param2){
$param2=3;
return $param2;
}
$param3=add(&$param1); //調用方法add,並將變量1的引用傳給變量2,此時兩個地址指向同一內存,改變其中一個,另外一個也要被改變;
echo $param1; //3,內存已在函數內部改變;
echo $param3; //3
?>
Example3:
<?php
//給數組里面的鍵值各增加10;
$arr = array(3,5);
foreach($arr as $k=>$v){
$v+=10;//1.更改無效,相當於遍歷出的鍵值扔給變量$v,然后更改變量$v的值,跟數組無關;
echo $v." ";//輸出13 15;
}
foreach($arr as $k=>$v){
$arr[$k]+=10;//2.更改有效,直接更改鍵名里面的值;
echo $v;//輸出3,5;
}
foreach($arr as &$v){
$v+=10;//3.更改有效,遍歷的鍵值直接給了$v的地址,這個地址其實就是鍵名..$v+10就等於$arr[$k]+10;
}
?>
然后我們來看一下這道面試題:
$a = 1;
$b = &$a;
unset($a);
echo $b; //??
當時我的回答是這樣的: 會報一個notice的錯誤,因為$b和$a共用一處內存,unset就把這處內存銷毀掉了,然后輸出$b變量的時候找不內存,所以應該是Notice: Undefined variable: b in
但是要注意: unset並沒有真正銷毀變量的作用...僅僅是切斷了變量與內存之間的關系,內存只要還被引用着就不會被釋放; $b和$a同時指向1,切斷其中$a的關系,$b還是指向1,所以上題不報錯,照樣輸出1。
另外補充一點: 在PHP中對象的傳值默認是引用傳值
再看一題:
在做這題之前我們回顧一下什么是析構函數,而PHP中對象銷毀的方式有哪些:
析構函數:對象銷毀時執行;注意在隱式銷毀中是在是所有php代碼執行完最后一行代碼的時候才銷毀,還有要注意在單入口文件下的MVC模式
對象的銷毀:
- 顯試銷毀: 當對象沒有被引用時就會被銷毀,所以我們可以unset或為其賦值NULL;
- 隱試銷毀:PHP是腳本語言,在代碼執行完最后一行時,所有申請的內存都要釋放掉.
Example1:
class Human {
public $name = '張三';
public $gender = null;
public function __destruct() {
echo '死了!<br />';
}
}
$a = new Human();
$b = $c = $d = $a;
unset($a);
echo '<hr />'; //析構函數究竟是觸發了幾次,是在線上觸發,還是在線下觸發?
答案:
$b = $c = $d = $a;默認引用傳值,四個變量指向同一處內存,unset的時候對象還是被還是其它三個變量使用,所以對象並沒有被銷毀,所以析構函數是在線下觸發的(代碼執行完了,內存自動釋放)
Example2:
class Human {
public $name = '張三';
public $gender = null;
public function __destruct() {
echo '死了!<br />';
}
}
$e = $f = $g = new Human();
unset($e);
unset($f);
unset($g);
echo '<hr />'; //同樣的問題: 析構函數是在線上觸發還是線下觸發?
我相信通過Example1的講解,應該很快知道答案是在線上觸發;在代碼運行完自動釋放內存之前由於對象已經沒有被任何變量引用所以就自動釋放了內存....
最后:
其實這家面試是我第一家面試,各方面都還挺滿意的,也談妥了;但是我還是很想多面試幾家,並不是為了去比價或者怎么的,很多時候我認為在面試中其實可以學到很多東西的。現在的人事小姐真的是巨能說話,架不住她的那種氣場就答應早點上班了,真心想多面試幾家~~唉!
