应用 IT 技术
解决实际问题

PHP引用变量详解 (附经典面试真题)

一、PHP 中引用变量的概念

在 PHP 中引用意味着用不同的名字访问同一个变量内容。通过在变量前使用 & 符号进行定义。

 

二、PHP 的 COW 机制

COW(Copy on Write)是内存优化的常见手段,在 PHP 中也采用了这种方式来优化内存。COW 即“写时复制”,只有当对其中一个或多个变量进行写操作的时候,才会复制一份内存,对其内容进行修改。

示例1:

<?php
  $a=range(0,1000);
  var_dump(memory_get_usage());  //int(204416)

  $b=$a;
  var_dump(memory_get_usage());  //int(204464)

  $a=range(0,1000);
  var_dump(memory_get_usage());  //int(288744)
?>

解析:
1. 对 $a 赋值时,开辟出了一块内存空间;
2. 当 $b=$a 操作时,并没有立即开辟出另一块新的内存空间,而是 $a 与 $b 指向了同一块内存(如图1所示),因而获取此时的内存使用情况几乎与第1步没有差别;

PHP的COW机制1图 1

3. 当对 $a 再进行赋值操作时,即便赋值没有变化,但此时 $a 开辟出了一块新的内存空间(图2),获取内存使用情况与前两步有了明显的差别。

PHP的COW机制2

图 2

 

三、PHP 引用变量的内存空间变化

将示例1中的 $b 改为对 $a 的引用变量,观察其内存空间变化情况。

示例2:

<?php
  $a=range(0,1000);
  var_dump(memory_get_usage());  //int(204416)

  $b=&$a;
  var_dump(memory_get_usage());  //int(204464)

  $a=range(0,1000);
  var_dump(memory_get_usage());  //int(204464)
?>

解析(图3):
1. 对 $a 赋值时,开辟出了一块内存空间;
2. 当 $b=&$a 操作时,$b 指向了与 $a 同一块的内存空间,获取此时的内存使用情况几乎与第1步没有差别;
3. 当对 $a 再进行赋值操作时,只更改了空间内存储的值,而不会开辟出一块新的内存空间,获取此时的内存使用情况几乎与前两步没有差别。

PHP引用变量内存空间

图 3

 

四、PHP 引用变量的 zval 变化

通过观察 zval 结构体的变化理解引用变量的底层原理。此处需用到 PHP 调试工具 Xdebug

示例3:

<?php
  $a=range(0,3);
  xdebug_debug_zval('a');  //refcount=1,is_ref=0

  $b=$a;
  xdebug_debug_zval('a');  //refcount=2,is_ref=0

  $a=range(0,3);
  xdebug_debug_zval('a');  //refcount=1,is_ref=0
?>

解析(图4):
1. 对 $a 赋值时,zval 显示有一个变量指向该变量容器(refcount=1),且非引用变量(is_ref=0);
2. 当 $b=$a 操作时,有两个变量指向该变量容器(refcount=2),且非引用变量(is_ref=0);
3. 当对 $a 再进行赋值操作时,由于 $a 开辟出了一块新的内存空间,此时又只有一个变量指向 $a 的变量容器(refcount=1),且非引用变量(is_ref=0)。即印证了前面第二部分里提到的 PHP 的 COW 机制。

PHP引用变量的zval变化1

图 4

示例4:

<?php
  $a=range(0,3);
  xdebug_debug_zval('a');  //refcount=1,is_ref=0

  $b=&$a;
  xdebug_debug_zval('a');  //refcount=2,is_ref=1

  $a=range(0,3);
  xdebug_debug_zval('a');  //refcount=2,is_ref=1
?>

解析(图5):
1. 对 $a 赋值时,zval 显示有一个变量指向该变量容器(refcount=1),且非引用变量(is_ref=0);
2. 当 $b=&$a 操作时,有两个变量指向该变量容器(refcount=2),且为引用变量(is_ref=1);
3. 当对 $a 再进行赋值操作时,由于引用变量不会开辟新的内存空间,此时还是有两个变量指向 $a 的变量容器(refcount=2),且为引用变量(is_ref=1)。即印证了前面第三部分里提到的 PHP 引用变量的内存空间变化情况。

PHP引用变量的zval变化2

图 5

 

五、PHP 中引用变量的其它几个注意点

1. unset 只会取消引用,不会销毁空间。

示例5:

<?php
  $a=1;

  $b=&$a;

  unset($b);

  echo $a;  //1
?>

解析(图6):

PHP的unset只会取消引用

图 6

2. 对象本身就是引用传递。

示例6:

<?php
  class Person
  {
    public $name='zhangsan'; 
  }

  $p1=new Person;
  xdebug_debug_zval('p1');  //refcount=1,is_ref=0

  $p2=$p1;
  xdebug_debug_zval('p1');  //refcount=2,is_ref=0

  $p2->name='lisi';
  xdebug_debug_zval('p1');  //refcount=2,is_ref=0,name='lisi'
?>

解析(图7):
1. 对 $p1 实例化,zval 显示有一个变量指向该变量容器(refcount=1),且非引用变量(is_ref=0);
2. 当 $p2=$p1 操作时,有两个变量指向该变量容器(refcount=2),且非引用变量(is_ref=0);
3. 当对 $p2->name 进行修改时,还是有两个变量指向 $p1 的变量容器(refcount=2),且 $p1->name 的值也被修改了,由此可知 $p2 并没有开辟新的内存空间,而依然与 $p1 指向同一个空间。is_ref=0 说明 $p1 不是引用变量,但是却进行了引用传递。

PHP对象的引用传递

图 7

 

六、考察 PHP 引用变量的经典面试真题

写出如下程序的输出结果:

<?php
  $data=['a','b','c'];

  foreach($data as $key=>$val){
    $val=&$data[$key];
  }
?>

1. 程序运行时,每一次循环结束后变量 $data 的值时什么?请解释。
2. 程序执行完成后,变量 $data 的值时什么?请解释。

解析(图8):
1. 第一次循环时,$key=0,$val=’a’ ;之后 $val=&$data[0],$val 与 $data[0] 指向了同一个空间,此时 $data=[‘a’,’b’,’c’] ;
2. 第二次循环时,$key=1,$val=’b’,由于指向同一空间 $data[0]=’b’ ;之后 $val=&$data[1],$val 与 $data[1] 指向了同一个空间,此时 $data=[‘b’,’b’,’c’] ;
3. 第三次循环时,$key=2,$val=’c’,由于指向同一空间 $data[1]=’c’ ;之后 $val=&$data[2],$val 与 $data[2] 指向了同一个空间,此时 $data=[‘b’,’c’,’c’] ;
4. 循环结束,最终 $data=[‘b’,’c’,’c’] 。

PHP引用变量面试真题

图 8

未经允许不得转载:酷睿N核博客 » PHP引用变量详解
支付宝打赏微信打赏

如果文章对您有所帮助,欢迎移至上方按钮打赏作者

分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址