Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/259.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么PHP';s foreach将其数组的指针(仅)前进一次?_Php - Fatal编程技术网

为什么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。