摘要:中基础中的三大坑,遍历,引用机制,数组。今天我们在讲讲中的一些奇怪现象。本文适合有一定基础的。运行流程共用一个结构体开始遍历数组,进行判断,拷贝数组是一个新的结构体,操作的是新的结构体。那么遍历数组时,全程与原数组无关。
PHP中基础中的三大坑,foreach遍历,引用机制&,数组。
今天我们在讲讲foreach中的一些奇怪现象。
在讲解之前,可以先看看我其他相关的文章,属于同一个大的知识点,都看看有助于理解。
当我们使用foreach时,内部究竟发生了什么?(PHP5)
PHP底层分析:关于写时复制(cow)
PHP底层分析:关于强制分裂
△△△写前声明:以下结论都基于PHP5版本,因为时代在进步,在PHP7中内部的结构体模块和引用模块均发生重大变化,PHP7的foreach输出规则也旋即发生变化。当然,由于PHP7想要普及还需要一到两年(今年是2016)时间,所以这篇文章还是有些价值的,至少可以让你先理解一下PHP内部实现。
有疑问可以直接在评论中抛出,我会一一解答。
本文适合有一定基础的PHPer。
那么开始上图,这是鸟哥(惠新宸,PHP7核心开发组成员,开发组中唯一的一个中国人[撒花])在Think 2015 PHP技术峰会上的一个演讲截图,他在讲述PHP5的foreach和PHP7的foreach区别,我们把他演讲中提及到PHP5的部分拿出来看看。
那么我们着重看下这幅图的三段代码执行流程。
我讲讲三段代码的运行原理
code1.php
$value) { var_dump(current($a)); //output int(2) int(2) int(2) } ?>
输出值为: int(2) int(2) int(2)。
同学们可能纳闷了,乍一看并没有发生明显的写时复制(相关文章)或者强制分裂(相关文章),怎么会是三个"2"呢。
关键点在于current()函数上:
foreach循环开始,拷贝一个数组出来,然后refcount_gc=2(foreach原理不太了解的同学,可以看看我的另一篇文章:当我们使用foreach时,内部究竟发生了什么?(PHP5) )
此时原数组($a)和拷贝数组(我这里命名为$a_copy)的指针均指向下标1,
随后进入大括号执行体,current()操作的参数必须是引用数组(红线部分),如果不是引用数组的话会强制转换成引用数组(即结构体中is_ref__gc从0 -> 1)。
根据强制分裂原理,一个结构体的is_ref__gc的值从0 -> 1的时候,如果refcount_gc=2时,就会发生url"强制分裂了"。
强制分裂后, 原数组($a)和拷贝数组(我这里命名为$a_copy)结构体已经不一样,但是foreach操作的是拷贝数组($a_copy),原数组被丢在半道上了,所以三次输出var_dump(current($a))均为2
code2.php
这段代码和code1.php原理差不多,只是在foreach前进行了一次引用赋值,结构体变化成:refcount_gc=2;is_ref_gc=1; 随后foreach遍历数组,此时原数组($a),拷贝数组(我这里命名为$a_copy)和$b的指针均指向下标1并且均为引用数负责(is_ref_gc)。
接着进入大括号执行体:var_dump(current($a)); 前面说道",current()操作的参数必须是引用数组(红线部分),如果不是引用数组的话会强制转换成引用数组(即结构体中is_ref__gc从0 -> 1)。"
此处参数$a已经为引用数组,不会发生强制分裂,原数组($a)和拷贝数组($a_copy)为同一个结构体,正常输入为:int(2) int(3) bool(false)
code3.php
$value) { var_dump(current($a)); //output int(1) int(1) int(1) } ?>
第三段代码和前面两个又有所不同了,这次$b=$a是使用传值赋值,那么此处为什么为3个int(1)呢?这次的原因在于foreach的机制:
foreach循环之前,需要判断数组的 refcount计数,如果大于1,拷贝数组自己成为一个新的结构体,循环过程操作的是新的结构体。
运行流程: line:3; // $a,$b共用一个结构体 line:4; //foreach开始遍历数组,进行refcount判断,拷贝数组是一个新的结构体,foreach操作的是新的结构体。那么遍历数组时,全程与原数组无关。 Line:5: //打印数组当前单元,由于原数组和拷贝数组已不是一个结构体,所以原数组的指针没变过,打印出int(1)。
如果还是有些不明觉厉的话,可以反复看多几遍,对了,再次推荐看下这几篇文章,都能理解的话对foreach掌握也差不多够用了。
当我们使用foreach时,内部究竟发生了什么?(PHP5)
PHP底层分析:关于写时复制(cow)
PHP底层分析:关于强制分裂
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/30347.html
摘要:如何证明我的说法呢可以用下面这段代码检验。那么在这里我们需要特别注意,为了保险起见我们在遍历数组后,最好手动一下数组,防止出错这样就正常了。还有一点手册也提醒我们了转成代码的意思就是遍历后和是真实存在的,最好使用后能手动掉。 以下所有结论均基于PHP5版本看下面一段最基础的foreach遍历数组代码。 输出为’0a1b2c’自然没有疑问,那么此过程中$arr,$key,$valu...
摘要:说到中的生成器,有人可能会想到协程,这里我们先不说如何实现协程,我们探究下的执行过程。如果函数包含了关键字的,那么函数执行后的返回值永远都是一个对象。如果函数内部同事包含和该函数的返回值依然是对象,但是在生成对象时,语句后的代码被忽略。 说到php中的Generator(生成器),有人可能会想到协程,这里我们先不说php如何实现协程,我们探究下Generator的执行过程。 Gene...
摘要:建议使用来将其销毁。那为何一直是,一直是呢先用查看编译后的新特性之循环对数组内部指针不再起作用在之前当数据通过迭代时数组指针会移动。版本结果说明数组指针会移动数据指针不再移动按照值进行循环时对数组的修改是不会影响循环。 招聘 标签(空格分隔): 招聘 PHP 国贸 语言基础 foreach 语法结构提供了遍历数组的简单方式。 php5之前, foreach仅能用于数组php5+, 利...
摘要:循环方法方法不改变原数组方法会给原数组中的每个元素都按顺序调用一次函数。筛选出过滤出数组中符合条件的项组成新数组代码方法方法为数组中的每个元素执行一次函数,直到它找到一个使返回表示可转换为布尔值的值的元素。 showImg(https://segmentfault.com/img/bV2QTD?w=1600&h=500); 前言 JavaScript 发展至今已经发展出多种数组的循环遍...
摘要:结果分析虽然我没有代码,但是我猜测是循环执行语句的多少差别。如果有更好的原因可以评论或者发起后话生命不息,技术不止。很多时候我也为了代码量的减少不理会运行时间的差异,这次吸收教训,之后在实际开发会更加注意时间。 本文首发于cartoon的博客 转载请注明出处:https://cartoonyu.github.io/cartoon-blog/post/java/for%E4...
阅读 3300·2021-11-16 11:45
阅读 4299·2021-09-22 15:38
阅读 2818·2021-09-22 15:26
阅读 3328·2021-09-01 10:48
阅读 768·2019-08-30 15:56
阅读 697·2019-08-29 13:58
阅读 1461·2019-08-28 18:00
阅读 2135·2019-08-27 10:53