为什么在foreach循环中没有修改数组的情况下会复制数组?

14
在一篇博客文章《PHP内部:foreach何时会复制》中,NikiC指出在像下面这样的代码中:

片段1

$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

$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)?

1
据我所知,可能不多,你的数组总是作为一个副本传递,因为你没有将它作为引用传递。 - JorgeeFG
1
@Jorge,问题是为什么PHP在代码片段1中只进行软复制(增加引用计数),而在代码片段2中进行硬复制?既然数组没有被修改,为什么我们不能在代码片段2中也进行软复制呢? - Pacerier
2
我读了那篇博客,我认为原因很清楚,因为$array变量在foreach循环发生的函数作用域中没有定义。这里有一个混淆点是,foreach不会复制 $array,更好的说法是它将被test()函数复制,但这并不完全正确。因为当foreach迭代数组时,它必须访问其内部指针以获取keyvalue,因此,它必须在副本或原始副本上工作。 - user1646111
1
@Akam,$array在test()函数中并没有进行硬复制,只是引用计数增加了,也就是所谓的软复制。 - Pacerier
1
@PeeHaa,实际上它还没有被回答。因此是一个问题。 - Pacerier
显示剩余4条评论
2个回答

2
这是因为在第二种情况中,$array以值的形式传递给test()函数。因此,在函数内部创建了$array的副本,并且foreach()循环遍历该副本。如果将$array作为引用传递给test()函数,则情况将不同。
有关按值传递和按引用传递的信息,请参见此问题

1
函数内部没有制作 $array 的硬拷贝,详见 http://derickrethans.nl/talks/phparch-php-variables-article.pdf。只有 xdebug_debug_zval 报告的引用计数增加了。由于没有制作副本,因此 foreach 无法在副本上工作。 - Pacerier

1
你的问题已经在你提供的文章中得到了回答。它在以下章节中给出:

未被引用,引用计数>1

解释为需要复制这些结构体,因为数组指针会移动,而这不能影响外部数组。请注意保留HTML标签。

1
你能展示一个它如何失败的例子吗?如果外部数组的内部指针改变了,这有什么关系吗?在第一个片段中,数组指针也会移动,并不需要硬拷贝。 - Pacerier
如果您将一个变量传递给函数,那么您期望在函数返回后该变量不会改变。更改数组指针也是对变量的更改,这是不应该发生的! - Sven
1
你说改变内部数组指针也是对变量的更改。那么为什么第一个代码片段不进行硬复制?第一个代码片段也修改了数组。在第一个代码片段中,$arrayforeach之前和之后的状态也是不同的。 - Pacerier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接