为什么PHP';s foreach将其数组的指针(仅)前进一次?
这是一个关于为什么PHP';s foreach将其数组的指针(仅)前进一次?,php,Php,这是一个关于foreach在PHP中实现方式背后的原因的好奇问题 考虑: $arr = array(1,2,3); foreach ($arr as $x) echo current($arr) . PHP_EOL; 这将输出: 2 2 2 我知道,foreach将数组指针倒带到开头;然而,为什么它只增加一次呢?魔盒里面发生了什么??这只是一件(丑陋的)艺术品吗 感谢@NickC——对于任何对zval和refcount感兴趣的人,您可以在第一次迭代之前阅读基础知识,$array是“软拷贝”
foreach
在PHP中实现方式背后的原因的好奇问题
考虑:
$arr = array(1,2,3);
foreach ($arr as $x) echo current($arr) . PHP_EOL;
这将输出:
2
2
2
我知道,foreach
将数组指针倒带到开头;然而,为什么它只增加一次呢?魔盒里面发生了什么??这只是一件(丑陋的)艺术品吗
感谢@NickC——对于任何对
zval
和refcount
感兴趣的人,您可以在第一次迭代之前阅读基础知识,$array
是“软拷贝”的,用于foreach
。这意味着不进行实际复制,但只有$array
的zval的refcount
增加到2
在第一次迭代中:
$x
中2
current
是通过引用传递的$array
调用的。由于引用,PHP无法再与循环共享zval
,需要将其分离(“硬拷贝”)$array
zval因此不再与foreach
zval相关。因此,它的数组指针不再被修改,current
始终返回相同的元素
顺便说一句,我已经写了一个小总结。它可能与上下文有关,但与问题没有直接关系,因为它主要涉及硬拷贝。如果我们稍微更改一下代码,看看有多有趣:
$arr = array(1,2,3);
foreach ($arr as &$x) echo current($arr) . PHP_EOL;
我们得到了这个输出:
2
3
一些有趣的参考资料:
现在,试试这个:
$arr = array(1,2,3);
foreach ($arr as $x) { $arr2 = $arr; echo current($arr2) . PHP_EOL; }
输出:
2
3
1
2 / 2
2 / 2
2 / 2
2 / 2
3 / 3
1 / 1
这确实很奇怪
那么这个呢:
$arr = array(1,2,3);
foreach ($arr as $x) { $arr2 = $arr; echo current($arr) . ' / ' . current($arr2) . PHP_EOL; }
echo PHP_EOL;
foreach ($arr as $x) { $arr2 = $arr; echo current($arr2) . ' / ' . current($arr2) . PHP_EOL; }
输出:
2
3
1
2 / 2
2 / 2
2 / 2
2 / 2
3 / 3
1 / 1
看起来发生的事情就像在NickC answer中写的那样,加上一个事实,当数组作为参数传递给当前的函数时,由于它是通过引用传递的,里面的某些东西确实修改了作为参数传递给它的数组…这是使用PHP5.3进行代码操作码分析的结果 请参见此示例: 行动数目:15 编译变量:!0=$arr!1=x美元
line # * op fetch ext return operands
---------------------------------------------------------------------------------
2 0 > INIT_ARRAY ~0 1
1 ADD_ARRAY_ELEMENT ~0 2
2 ADD_ARRAY_ELEMENT ~0 3
3 ASSIGN !0, ~0
3 4 > FE_RESET $2 !0, ->13
5 > > FE_FETCH $3 $2, ->13
6 > ZEND_OP_DATA
7 ASSIGN !1, $3
8 SEND_REF !0
9 DO_FCALL 1 'current'
10 CONCAT ~6 $5, '%0A'
11 ECHO ~6
12 > JMP ->5
13 > SWITCH_FREE $2
14 > RETURN 1
有关详细信息,请参见NikiC的答案,但您可以在第8行看到!0在回路中永不改变。(5-12)回波电流($arr)是什么?在foreach循环中没有使用$arr。foreach($arr as$x)回波电流($arr).PHP\u EOL;哈——是的,为了美观起见,我试图优化我的循环,但后来拿出了一个关键的部分
foreach
对数组的副本进行操作。我不知道为什么它会改变数组指针。我原以为它会产生1
,因为我认为它会在副本上运行。但是我重新读了一遍,但是输出应该是123
或者1111
但不是222
。很好的问题!这不是复制品。链接的问题是“我在foreach中做了数组,但所有东西都坏了?!?让它消失”
,这是“我想要一个关于foreach循环行为的PHP内部工作的技术解释”
在第一次迭代中:reset+next。这是有案可查的。但是试图通过ref迭代和在循环中使用reset()来愚弄它,但是没有成功。我想这是一种保护措施。@MarkTomlin它不会,因为foreach会将其视为一个表达式而不是一个变量。我不太清楚为什么hakre会将它添加到答案中。我认为refcount
实际上增加到了3,因为foreach
头中有$arr(foreach(
@PhilippeGerber是的,在循环中转储调试(当前之前)给出了refcount 4,因此在循环开始后它必须是3。你知道额外的引用来自哪里吗?我在FE#u reset中只找到了一个ADDREF。我认为你可以将对意外/未定义行为的追求缩小到PHP 5.2.2和5.2.4之间的更改-可能与所有关于修复错误41的引用有关372(源数组的内部指针在数组复制期间重置)。
-在此之前,所有版本都返回了1
,例如OPs。