我知道在通过引用循环时不应该修改数组的物理结构,但我需要解释代码中发生的情况。让我们开始吧。

$x= [[0],[1],[2],[3],[4]];
foreach ($x as $i => &$upper) {
    print $i;
    foreach ($x as $j => &$lower) {

        if($i == 0 && $j == 2) {
            unset($x[2]);
        } else if($i == 1 && $j == 3) {
            unset($x[3]);
        }
    }
}

输出为01。奇怪的是,对于索引01,外循环只迭代两次。我在期待输出是014
我读过很多关于使用数组引用的危险的博客文章和问题,但没有什么能解释这种现象。我要为此伤脑筋好几个小时了。
编辑:
上面的代码是最小的可重复代码。有一种解释(但不正确)可能是这样的:
在内部指针设置为index2之前,外部循环经过两次迭代。但是循环在index2处找不到任何元素,因此认为没有元素被留下并退出。
这个理论的问题是它不能很好地解释这段代码:
$x= [[0],[1],[2],[3],[4]];
foreach ($x as $i => &$upper) {
    print $i;
    foreach ($x as $j => &$lower) {

        if($i == 0 && $j == 2) {
            unset($x[2]);
            // No if else here
            unset($x[3]);
        }
    }
}

同样地,上面的代码也应该产生01,但是它的实际输出是014,正如预期的那样。即使删除了一个系列中的两个项,php也知道仍然是需要迭代的元素。这可能是php脚本引擎的一个bug吗?

最佳答案

复制问题的简单代码:

$x = [0, 1, 2];
foreach ($x as $k => &$v) {
    print $k;
    if ($k == 0) {
        unset($x[1]);
    }
    end($x); // move IAP to end
    next($x); // move IAP past end (that's the same as foreach ($x as $y) {} would do)
}

如果foreach覆盖一个数组,它将被复制(=迭代时没有问题,您将迭代整个原始数组)。
但如果通过引用foreach,则不会复制数组(引用需要与原始数组匹配,因此无法复制)。
foreach在内部总是保存下一个要迭代的元素的位置。
但是当数组的下一个位置被删除时,foreach需要返回到数组并检查它的内部数组指针(iap)。
在这种情况下,下一个位置被破坏,IAP超过结束,它结束循环。
这就是你在这里看到的。
同样有趣的是:hhvm在这里的行为与php不同:http://3v4l.org/81rl8
附录:无限foreach循环:
$x = [0,1,2];
foreach ($x as $k => &$v) {
    print $k;
    if ($k == 1) {
        unset($x[2]);
    } else {
        $x[2] = 1;
    }
    reset($x);
}

如果你理解我上面的解释,猜猜为什么会无限期地循环。

09-07 13:20