Why does foreach copy the array when we did not modify it in the loop?
在"php内部:foreach什么时候复制"的博客文章中,nikic在这样的代码中指出:
片段1
1 2 3 4
| $array = range(0, 100000);
foreach ($array as $key => $value) {
xdebug_debug_zval ('array'); // array is not copied, only refcount is increased
} |
foreach不会复制数组,因为foreach修改的关于$array的唯一内容是它的内部数组指针。
他还指出,在这样的代码中:
片段2
1 2 3 4 5 6 7 8
| $array = range(0, 100000); // line 1
test ($array);
function test ($array) {
foreach ($array as $key => $value) { // line 4
xdebug_debug_zval ('array'); // array is copied, refcount not increased
// ...
}
} |
foreach将复制数组,因为如果没有,第1行中的$array变量将被更改。
但是,foreach修改的关于$array的唯一内容是它的内部数组指针。那么,如果第1行中的$array变量的内部数组指针发生了更改,为什么这一点很重要?在代码片段1中并不重要,为什么在代码片段2中如此重要?
为什么foreach需要复制片段2中的数组,即使我们没有在循环中修改它?
- 据我所知,这可能不多,您的数组总是作为副本传递,因为您没有将其作为引用传递。
- @Jorge,关键是为什么php只能在代码段1中软拷贝(增加refcount),而在代码段2中硬拷贝?既然数组没有修改,为什么我们不能在代码段2中进行软拷贝呢?
- @pacerier php.net/manual/en/language.references.pass.php
- @Peehaa,这解释了第一个片段,但不是第二个。
- 实际上,它是在@nikic's answer on so中回答的,也可以看到注释(前两个)。
- 我读了那篇博客,我认为原因很清楚,因为$array变量没有在foreach发生的函数范围内定义,这里的一个混淆是,foreach不会copy和$array两个变量,最好说它会被test() function复制,这不完全正确。因为当foreach迭代数组时,它必须有权访问它的内部指针来获取key和value,因此它必须在副本或原始副本上工作。
- @AKAM,$array不是由test()函数硬拷贝的,只有refcount会增加,即软拷贝。
- @Peehaa,实际上没有人回答。这就是问题所在。
- 我觉得@akam的解释很好。我会暂时忽略整个copy-on-write,然后这样看它:在第一种情况下,您是在数组上循环的,所以预期IAP会改变。在第二种情况下,数组在传递给函数时被复制(只有函数中的副本更改了IAP)。由于cow的存在,事情变得更加复杂,因为数组只在迭代过程中被复制,而不是在调用函数时被复制,但是其思想保持不变。
这是因为在第二种情况下,$array是按值传递给函数test()。因此,在函数内部生成了$array的副本,而foreach()在副本上工作。如果通过对函数test()的引用传递$array,情况会有所不同。
有关传递值与传递引用的信息,请参见此问题
- 函数derickrethans.nl/talks/phparch-php-variables-article.pdf中没有生成$array的硬拷贝。只有refcount根据xdebug_debug_zval的报告增加。因为没有复制,所以foreach对副本不起作用。
你的问题在你链接的文章中得到了回答。本节给出了
Not referenced, refcount > 1
其中解释了由于数组指针移动而需要结构的副本,并且这不得影响外部数组。
- 你能举个例子说明它是如何失败的吗?为什么外部数组的内部指针改变了呢?在第一个片段中,数组指针也会移动,并且不会强制在那里进行硬拷贝。
- 如果将变量赋给函数,则在函数返回后,期望变量保持不变。更改数组指针也是对变量的一个更改,而这绝对不会发生!
- 您认为更改内部数组指针也是对变量的更改。那么为什么第一个片段不做硬拷贝呢?第一个片段还修改了数组。在第一个代码片段中,foreach之前和foreach之后的$array状态也不同。