最近面试多,基本上一天一面试。遇到一个关于php赋值传值、引用传值及unset的问题,以前没有深究,百度了下发现道道还不少。
面试题:请问以下输出的结果是?
- Class User {
- public $name;
- }
- $user1 = new User();
- $user2 = $user1;
- $user1 -> name = 'one';
- $user2 -> name = 'two';
- $array1 = array();
- $array2 = $array1;
- $array1['name'] = 'one';
- $array2['name'] = 'two';
- echo $user1->name;//two
- echo $user2->name;//two
- echo $array1['name'];//one
- echo $array2['name'];//two
- //个人理解是,对象默认传值方式是引用传值,所以前面2个输出two
- //而下面2个传值方式是赋值传值,所以各不影响。
二篇相关文章,值得一读:
篇一:PHP的赋值传值与引用传值以及unset
首先,要理解变量名存储在内存栈中,它是指向堆中具体内存的地址,通过变量名查找堆中的内存;
普通传值,传值以后,是不同的地址名称,指向不同的内存实体;
引用传值,传引用后,是不同的地址名称,但都指向同一个内存实体;改变其中一个,另外一个就也被改变;
以下我将通过三个列子来详细讲解这两个传值的区别:
Example1:
- //普通传值
- $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:
- //函数中的普通传值 ↓↓
- $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:
- //给数组里面的键值各增加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; //??
注意: 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的讲解,应该很快知道答案是在线上触发;在代码运行完自动释放内存之前由于对象已经没有被任何变量引用所以就自动释放了内存....
经典评论:
从某种程度上说,变量是由变量名和值地址组成的;变量名是我们代码中可以看到的那个字母,而变量值是保存到了一个我们看不到的内存地址里;能理解变量名、变量值、值地址这三者的关系就可以理解引用传值、赋值传值已经删除变量的内部实行机制了
篇二:PHP中对象是按值传递还是按引用传递?
按值传递就是仅仅把值传递过去,相当于传递的是值的拷贝,而按引用传递传递的是内存的地址。
在 PHP5 中,如果按引用传递,就是将 zval 的地址赋给另一个变量,这时,两个变量都同时指向一个 zval 。而按值传递则是复制一个 zval,两个变量指向两个不同的 zval 。(为了简化,这里忽略 copy on write)
1.按引用传递的特点:
- $a = 2;
- $b = &$a; //按引用传递
- $a = 1;
- echo $b; //1
- $b = 3;
- echo $a; //3
2.现证明PHP中对象不是按引用传递:
- class Test{
- public $a ;
- }
- $test1 = new Test();
- $test2 = $test1;
- $test1 = null;
- var_dump($test1); // null
- var_dump($test2); //object 如果是按引用传递,那么$test2也应该为空!
3.但是,传递对象的时候,也出现了引用传递的特点:
- class Test{
- public $a ;
- }
- $test1 = new Test();
- $test1->a = '1';
- $test2 = $test1;
- $test2->a = '2';
- echo $test1->a; //2
- $test1->a = '3';
- echo $test2->a; //3
出现这种情况的原因,是因为在 PHP5 中,真正保存对象的结构是 zend_object,一个变量(zval)里实际保存的是指向该结构体的指针。传递这个变量时,就是拷贝该指针的副本,而这个指针指向存储堆区的对象。
其实,PHP 中所有的传递都是按值传递的,这点与 C、JavaScript 等语言相同。要想按引用传递,则必须使用 & 。
参考文章:
https://www.cnblogs.com/nixi8/p/4925094.html
http://blog.csdn.net/stfphp/article/details/52132348
评论