从一道面试题开始

在开始本节内容前,我们先来看看一道还算比较常见的PHP面试题:

1 $arr array('1','2','3');
2  
3 foreach($arr as &$v){
4 }
5  
6 foreach($arr as $v){
7 }
8  
9 var_dump($arr);

猜一下,运行的结果会是什么呢?熟悉PHP的同学可能已经知道结果了:

1 array
2   0 => string '1' (length=1)
3   1 => string '2' (length=1)
4   2 => &string '2' (length=1)
  •  
    奇怪了,为什么不是输出 1, 2, 3 呢?遍历操作也会改变原数组的值啊,我第一次听说呢。

是引用在捣鬼

那么为什么是1,2,2呢。让我们一步步来看:

我们知道对数组执行foreach循环时,是通过移动数组内部指针来实现的。对于上面的例子:当foreach循环结束的时候,由于$v为引用变量,所以$v 与 $arr[ 2 ] 指向了同一个地址空间(共享变量值),之后对$v的任何修改都会直接反映到数组$a中。

要不我们在程序中加入调试代码,看看运行过程的情况吧。

1 $arr array('1','2','3');
2  
3 foreach($arr as &$v){
4 }
5  
6 foreach($arr as $v){
7     var_dump($arr); 
8     echo "<br/>";
9 }

程序运行的结果是:

01 array
02   0 => string '1' (length=1)
03   1 => string '2' (length=1)
04   2 => &string '1' (length=1)
05  
06 array
07   0 => string '1' (length=1)
08   1 => string '2' (length=1)
09   2 => &string '2' (length=1)
10  
11 array
12   0 => string '1' (length=1)
13   1 => string '2' (length=1)
14   2 => &string '2' (length=1)

简单解释一下:

  1. 在第一次 foreach 循环里面,$v 是个引用。在循环结束之后,$v 与 $arr[2] = 3,指向了同一个内存块;
  2. 紧接着第二次循环,$v 引用并没有改变,还是$arr[2]的引用,这时 $v 值是 $arr[0] 的值,所以导致 $arr[2] = 1;
  3. 同上面,$v 已被 $arr[1] 影响,其值为 2,导致 $arr[2] = 2;
  4. 所以最终结果是 1, 2, 2.

关于引用,你需要了解的

1. 引用类似于指针,但是不同于指针。

下面的操作让 $a 和 $b 指向了同一个内存块,值为 “str”

1 $a "str";
2 $b = &$a;
3  
4 echo $a;
5 echo '<br />';
6 echo $b;

试下更改$a和$b中任何一个元素的值。另外一个值都为随之改变:

1 $a "str";
2 $b = &$a;
3  
4 $b "www.nowamagic.net";
5  
6 echo $a;
7 echo '<br />';
8 echo $b;

程序运行结果为:

1 www.nowamagic.net
2 www.nowamagic.net

2. 引用作为函数参数传递时,是可以被函数内部更改的:

01 $a "str";
02 $b = &$a;
03  
04 function change(&$a){ 
05     $a "www.nowamagic.net";
06
07  
08 change($a);
09  
10 echo $a;
11 echo '<br />';
12 echo $b;

程序运行结果为:

1 www.nowamagic.net
2 www.nowamagic.net

3. unset只会删除变量。并不会清空变量值对应的内存空间:

1 $a "str";
2 $b = &$a;
3  
4 unset($b);
5  
6 echo $a;
7 echo '<br />';
8 echo $b;

程序运行结果为:

1 str
2 Notice: Undefined variable: b
  •  
    PHP的引用有上面的特点,在编码的过程中,要小心使用引用。防止陷入莫名其妙的尴尬。PHP采用的复制机制是“引用计数,写时复制”,也就是说,即便在PHP里复制一个变量,最初的形式从根本上说其实仍然是引用的形式,只有当变量的内容发生变化时,才会出现真正的复制。所以才会出现上面的问题。之所以这么做是出于节省内存消耗得目的,同时也提升了复制的效率。

在 foreach 里使用引用要注意的陷阱(转)的更多相关文章

  1. CentOS磁盘用完的解决办法,以及Tomcat的server.xml里无引用,但是项目仍启动的问题

    这是我2018年的第一篇博客...人真是懒了啊...最近在写微信小程序,觉得小程序做的也... 好了不吐槽了,言归正传 前言: 由于我之前不是买了个三年的香港服务器么 , 之前广州2的服务器我就没有续 ...

  2. foreach里的按引用传值问题

    1.foreach($arr as $k=>&$v){ } 这样循环时候最后一个结果前边会有&,出现输出不了的情况,这时候只需要加一个unset($v),加在循环里和外均可. 2 ...

  3. <c:if>判断两个<c:forEach>里的数据是否相等

    问题:两个<c:forEach>嵌套,里面循环的值和外面的值进行比较(里层里的PARENTID是否等于外层的ID),如果相等就显示. <c:forEach items="$ ...

  4. [PHP] foreach循环的引用赋值可能导致的问题

    foreach($arr as &$value)1.引用赋值符号&,是每次循环的时候,把当前元素变成地址,$value变量就是对应元素的地址,循环结束$value是一个指向最后一个元素 ...

  5. php在foreach中使用引用赋值&可能遇到的问题(转)

    楼主在写项目的时候,由于初涉PHP的赋值引用操作,觉得这个功能非常强大,用时一时爽,没有深入了解过其中的原理,导致了一些当时觉得不可思议的BUG,废话不都说,我举个例子详细的描述一下这个问题. 代码: ...

  6. Python里的引用与拷贝规律

    python的可变不可变与各种浅拷贝深拷贝规则,一并梳理. Python一切皆引用 在C++/Java里,int a = 1就是创建变量为a,赋值为1:int b = a就是创建变量b,赋值为a的值. ...

  7. jsp页面中从forEach里向action里面传递其中的一个对象

    <c:forEach var="user" items="${users }"> <form action="user_update ...

  8. 如何获取c:forEach里面点击时候的值

    1.c:forEach遍历输出 <c:forEach items="${data}" var="item" > <a onclick=&quo ...

  9. 在mvc4里怎样引用:System.Web.Optimization和entityframework

    请在nuget 里运行: Install-Package Microsoft.AspNet.Web.Optimizationinstall-package entityframework

随机推荐

  1. Entity Framework 级联删除

    为一对主从表增加级联删除功能 protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.E ...

  2. 面试后 follow up letter 分享

    分享一下最近面试外企的follow up letter. Dear Mr. Xu,     Thank you again for the time you and Mr. Guo spent wit ...

  3. 【技术宅3】截取文件和url扩展名的N种方法

    //截取文件扩展名的N种方法   //第1种 //strrchr() 函数查找字符在指定字符串中最后一次出现的位置,如果成功,则返回其后面的字符串 //返回带有点的扩展名 function get_e ...

  4. GlusterFS简单配置

    1.准备工作 准备三台机器(物理机或者虚拟机均可)用于安装和测试GlusterFS,其中两台用作服务器,一台用作客户端,主机名分别为: Server1.zhaogang.int  10.0.21.24 ...

  5. python中归并排序

    # coding=UTF-8 #!/usr/bin/python import sys def merge(nums, first, middle, last): "merge" ...

  6. Day12(补充) Python操作MySQL

    本篇对于Python操作MySQL主要使用两种方式: 原生模块 pymsql ORM框架 SQLAchemy pymsql pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb ...

  7. BZOJ 1028 麻将

    Description 麻将是中国传统的娱乐工具之一.麻将牌的牌可以分为字牌(共有东.南.西.北.中.发.白七种)和序数牌(分为条子.饼子.万子三种花色,每种花色各有一到九的九种牌),每种牌各四张.在 ...

  8. Ubuntu_10.04下Hadoop-0.20.2集群配置手册

    Ubuntu_10.04下Hadoop-0.20.2集群配置手册 一.软硬件环境的准备 下面的文章来自hadoopor.com,我先交待一下我自己的环境: 两台机器,每台机器上面两个虚机(vmware ...

  9. DB2中SQLSTATE=57016 SQLCODE=-668

    执行 alter table DW_RPT.TRPT_JV_COGNOS_RPT add CENTER_CD varchar(10) ALTER TABLE DW_RPT.TRPT_JV_COGNOS ...

  10. ajax异步请求不能刷新数据的问题

    搞了两三天的问题,今天解决了.总结下: function reportGrpChange(cuid, title){ document.getElementById('reportFrameDiv') ...