首页 > php foreach

php foreach


为啥是b呢


<?php
$a = array(1,2,3);
foreach ($a as $key => $value) {
    var_dump(current($a));     //output int(2) int(2) int(2)
}
?>


我们都知道,foreach循环开始的时候,拷贝了一个数组出来,然后refcount_gc=2(这个不懂的话可以看下https://.com/a/11...

此时原数组($a)和拷贝数组(我这里命名为$a_copy)的指针均指向下标1,随后进入大括号执行体,但是$arr[$k] = $v又再次赋值,发生了写时复制,结构体就分裂了。

所以题主的代码中,触发分裂后$arr与循环的$arr_copy(先这样命名)是不同的结构体了,循环的$arr_copy的下标跟着循环改变而$arr的下标是刚进来时候的1


php的数组都有一个内部指针,指向数组的元素,初始化的时候是第一个。
当你foreach遍历的时候,内部指针向后移动了一位就再也没动过了。。。
所以当前的指针就在第二位了!


先贴一段foreach的C源[伪]代码吧。代码转自鸟哥博客——20 Nov 08 深入理解PHP原理之foreach 。

    zend_op *foreach_copy;
    zend_op *fetch = &CG(active_op_array)->opcodes[foreach_token->u.opline_num];
    zend_op *end = &CG(active_op_array)->opcodes[open_brackets_token->u.opline_num];
 
    /* Change "write context" into "read context" */
    fetch->extended_value = 0;  /* reset ZEND_FE_RESET_VARIABLE */
    while (fetch != end) {
        --fetch;
        if (fetch->opcode == ZEND_FETCH_DIM_W && fetch->op2.op_type == IS_UNUSED) {
            zend_error(E_COMPILE_ERROR, "Cannot use [] for reading");
        }
        fetch->opcode -= 3; /* FETCH_W -> FETCH_R */

楼上有的人也说过了,源代码也写的很清楚。
会有一个foreach_copy 存在。
当然,源代码中可以很清楚的看到整个过程:
实现foreach的核心就是如下3个函数:
zend_do_foreach_begin
zend_do_foreach_cont
zend_do_foreach_end
,然而我C语言不是很熟,那么我们直接看PHP的官方文档。

The first form loops over the array given by array_expression. On each iteration, the value of the current element is assigned to $value and the internal array pointer is advanced by one (so on the next iteration, you'll be looking at the next element).
Note:
In PHP 5, when foreach first starts executing, the internal array pointer is automatically reset to the first element of the array. This means that you do not need to call reset() before a foreach loop.
As foreach relies on the internal array pointer in PHP 5, changing it within the loop may lead to unexpected behavior.
In PHP 7, foreach does not use the internal array pointer.

简单点说,就是foreach会自动将数组内部指针(pointer)指向第一个元素(element),并且每次循环的时候指针前移(advanced by one)。
所以在我们大多数人认为的情况下,运行如下代码:

<?php
$arr = [1, 2, 3, 4];
foreach ($arr as $key => $value) {
    echo "\r\n $key => $value  current arr = " ;
    var_dump($arr);//current($arr);
    echo " \r\n";
    if ($value == 3) break;
}
echo "\r\n $key => $value  current arr = " . current($arr) . " \r\n"; // 4

我们最终得到的结果应该是:
2 => 3 current arr = 4,如果把 if ($value == 3) break;注释,得到 2 => 3 current arr = false.

现在再看楼主的问题,为什么得到的是b。
将上述代码中的 var_dump($arr);改成类似的代码 $arr[$key] = $value.
运行,结果:
2 => 3 current arr = 4,如果把 if ($value == 3) break;注释,得到 2 => 3 current arr = 2.
即,楼主答案中的b.
同样的,我们把$arr[$key] = $value改成 current($arr),也会得到
2 => 3 current arr = 4,如果把 if ($value == 3) break;注释,得到 2 => 3 current arr = 2.

显然,就算没有赋值操作(我们用current函数得到的是2,用var_dump()函数得到的是4),也会改变我们的结果。

最后给出总结,当我们使用foreach的时候,会产生一个foreach_copy,如果我们在循环中对$arr调用了会导致内存地址变化的函数(例如current),为了避免对原数组(指针指向的内存地址)的修改,会导致接下来的循环使用foreach_copy去完成操作。因此原数组的curren指针就不会改变。

同理,如果加上引用&$value,那么原数组就会被修改,指针位置就会发生改变。


foreach( $arr ) // $arrCopy1 = $arr
current( $arr ) // $arrCopy2 = &$arr

当$arrCopy1 = $arr 时,他俩公用同一份内容空间,所以可以同步赋值,
当$arrCopy2 = &$arr 时,$arr被引用了,必须给$arrCopy1再分配一个内存空间,所以$arrCopy1与$arr不同步了,因为$arrCopy1与$arr同步。

所以foreach操作$arrCopy1时,$arr就不变了

【热门文章】
【热门文章】