• 技术文章 >后端开发 >php教程

    PHP 循环中「引用」引发的奇怪问题

    2016-07-25 08:46:29原创476

    本文整理自 stackoverflow 网站上的一篇文章 Strange behaviour after loop by reference - Is this a PHP bug? —— 在 PHP 循环中,如果使用 引用 会引发非常奇怪的行为 - 这是 PHP 的一个 bug 吗?

    问题

    在我写一个简单的 PHP 脚本时,发生了一些非常奇怪的现象。下面是我的代码,为了清楚的表达我的意思,我特意去掉了一些不必要的代码:

    1. $arr = array("foo",
    2. "bar",
    3. "baz");
    4. foreach ($arr as &$item) { /* do nothing by reference */ }
    5. print_r($arr);
    6. foreach ($arr as $item) { /* do nothing by value */ }
    7. print_r($arr); // $arr has changed....why?
    复制代码

    输出如下:

    1. Array
    2. (
    3. [0] => foo
    4. [1] => bar
    5. [2] => baz
    6. )
    7. Array
    8. (
    9. [0] => foo
    10. [1] => bar
    11. [2] => bar // 错误发生??
    12. )
    复制代码

    这是 PHP 的一个 bug 吗?PHP 中为什么会发生如此古怪的行为呢?

    解析

    在第一个 foreach 循环结束后,$item 仍然引用(reference)着数组的最后一个元素,也就是 $arr[2]。 因此,当开始第二个循环的时候,$item 变量每次循环都会被赋一个新值。 在 php 中,如果一个内存空间是被引用的,那么当改变它的时候是直接改变这块内存空间的值。 当改变 $item 的时候,其实也改变了 $arr[2] 的值。

    因此,在第二个循环中:

    第一次循环,$item 和 $arr[2] 的值变成 $arr[0],也就是 'foo'。 第二次循环,$item 和 $arr[2] 的值变成 $arr[1],也就是 'bar'。 第三次循环,$item 和 $arr[2] 的值变成 $arr[2],也就是 'bar'($arr[2] 的值不是 'baz',因为在第二次循环中变成了 'bar')。

    'baz' 的值实际是在第二个循环中丢失了。

    译注:我不喜欢把 reference 翻译成「引用」,当然了,更不能翻译成「参考」了。每次我像别人解释 reference 时,都会告诉他: reference 就是 alias。 比如你叫吴毅昌(呵呵,无异常),二狗子是你的别名。本着好兄弟好基友的情谊:“来,二狗子,这 100 块钱给你吧。” 你——吴毅昌——回家一模口袋,多了 100 块钱。 @justjavac

    调试输出

    我们可以修改代码来调试并跟踪循环的执行细节。 我们可以输出 $item 的值,并且递归的输出数组 $arr。

    当第一个循环运行时,我们可以看到这样的输出:

    1. foo
    2. Array ( [0] => foo [1] => bar [2] => baz )
    3. bar
    4. Array ( [0] => foo [1] => bar [2] => baz )
    5. baz
    6. Array ( [0] => foo [1] => bar [2] => baz )
    复制代码

    在循环结束后,$item 和 $arr[2] 指向同一个内存区域。

    当第二个循环运行时,我们看到这样的输出:

    1. foo
    2. Array ( [0] => foo [1] => bar [2] => foo )
    3. bar
    4. Array ( [0] => foo [1] => bar [2] => bar )
    5. bar
    6. Array ( [0] => foo [1] => bar [2] => bar )
    复制代码

    在这次循环中,需要注意随着每次 $item 被赋予一个新值, $arr[2] 也会被赋值为和 $item 相同的值,因为它们都仍然指向相同的内存空间(译注:原文写的是 $arr[3],疑为原作者笔误。@justjavac)。 当循环到达数组的第三个值时,它包含的值是 bar,因为它的值在前两次循环中,被修改了。

    还有疑问

    也许你觉得,我仅仅是执行了一个空循环 foreach ($arr as &$item){},循环体里面什么都没有做,为什么数组元素却改变了?

    可能你觉得这个代码应该等价于

    1. for ($i = 0; $i < count($arr); $i++) {
    2. // do nothing
    3. }
    复制代码

    其实不对,代码应该等价于:

    1. for ($i = 0; $i < count($arr); $i++) {
    2. $item = $arr[$i];
    3. }
    复制代码

    也就是说, 在 foreach 循环中,隐含了一个赋值运算,唯一不同的时, 在赋值过程中,我们使用了引用,所以在第一个循环中,无意中修改了正在循环的数组内部的元素。

    PHP


    php入门到就业线上直播课:查看学习

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。

    前端(VUE)零基础到就业课程:点击学习

    清晰的学习路线+老师随时辅导答疑

    自己动手写 PHP MVC 框架:点击学习

    快速了解MVC架构、了解框架底层运行原理

    专题推荐:PHP 循环中「引用」引发的奇怪问题
    上一篇:php+redis缓存类 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• ❤️‍🔥接口自动化测试不想写代码?• 你知道如何用PHP实现多进程吗• PHP与MySQL连接的方法总结• 工具包分享:PHP实现滑块验证图片• 求解:phpcms模板怎样转码?该怎么解决• 哪位高手帮小弟我匹配下URL
    1/1

    PHP中文网