【问题标题】:PHP Pass by reference in foreach [duplicate]PHP在foreach中通过引用传递[重复]
【发布时间】:2011-03-19 10:56:34
【问题描述】:

我有这个代码:

$a = array ('zero','one','two', 'three');

foreach ($a as &$v) {

}

foreach ($a as $v) {
  echo $v.PHP_EOL;
}

有人可以解释为什么输出是: 零一二二。

来自 zend 认证学习指南。

【问题讨论】:

  • unset($your_used_reference); 每次使用foreach($var as &$your_used_reference)!
  • 感谢您的指点,但我只是想了解第二个 foreach 的工作原理。
  • 为什么 $your_used_reference(或 OPs 帖子中的 $v)不会自动取消设置?它的范围是 foreach,不应该存在于它之外?
  • @Hurix 循环在 PHP 中没有自己的作用域。
  • 这个问题现在是discussed on Meta。

标签: php


【解决方案1】:

我不得不花费几个小时来弄清楚为什么 a[3] 在每次迭代中都会发生变化。这是我到达的解释。

PHP 中有两种类型的变量:普通变量和引用变量。如果我们将一个变量的引用分配给另一个变量,则该变量将成为引用变量。

例如在

$a = array('zero', 'one', 'two', 'three');

如果我们这样做

$v = &$a[0]

第 0 个元素 ($a[0]) 成为引用变量。 $v 指向该变量;因此,如果我们对$v 进行任何更改,它将反映在$a[0] 中,反之亦然。

现在如果我们这样做

$v = &$a[1]

$a[1] 将成为一个引用变量,$a[0] 将成为一个普通变量(由于没有其他人指向$a[0],它被转换为普通变量。PHP 足够聪明,可以在以下情况下将其变为普通变量没有其他人指向它)

这是第一个循环中发生的事情

foreach ($a as &$v) {

}

在最后一次迭代之后$a[3] 是一个引用变量。

由于$v 指向$a[3],因此对$v 的任何更改都会导致对$a[3] 的更改

在第二个循环中,

foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}

在每次迭代中,$v 发生变化,$a[3] 发生变化。 (因为$v 仍然指向$a[3])。这就是$a[3] 在每次迭代中都发生变化的原因。

在最后一次迭代之前的迭代中,$v 被赋值为“二”。由于$v 指向$a[3],$a[3] 现在得到值“二”。请记住这一点。

在上一次迭代中,$v(指向$a[3])现在的值为“二”,因为在上一次迭代中$a[3] 被设置为二。 two 已打印。这解释了为什么在最后一次迭代中打印 $v 时重复“两个”。

【讨论】:

  • 我喜欢你的解释,但这仍然是让我经常感到困惑的事情之一。我必须记得取消设置以保持理智。 :-)
  • 对我来说,reference variable 是reference,在本例中为$v。另一方面,$a 始终是一个普通变量,在我看来不是参考变量。因此,PHP 也不是“聪明”的,因为它知道变量没有对它的引用,因为变量本身是否在某处被引用并不重要。要理解的重要一点是$v 是一个引用而不是一个变量。否则一个很好的解释。
  • 非常感谢!!很好很好的解释!
【解决方案2】:

因为在第二个循环中,$v 仍然是对最后一个数组项的引用,所以每次都会被覆盖。

你可以这样看:

$a = array ('zero','one','two', 'three');

foreach ($a as &$v) {

}

foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}

如您所见,最后一个数组项采用当前循环值:'zero'、'one'、'two',然后它只是 'two'... :)

【讨论】:

  • 好的,这意味着在最后一次迭代中,最后一项或仍然是两个,或者它再次被分配两个,为什么不是三个,为什么它在 last-1 处停止? :)
  • 它不会停止。最后一个数组项分配有当前循环值。所以它被分配“零”,然后是“一”,然后是“二”。在最后一次迭代中,由于上一次迭代,它被赋予了自己的值,即“二”。所以它只是保持“两个”。
  • 改进输出后我能够理解:$v gets the item [0](zero). $a[3] is now zero $v gets the item [1](one). $a[3] is now one $v gets the item [2](two). $a[3] is now two $v gets the item [3](two). $a[3] is now two
  • 这太反直觉了。对我来说,离开循环时 $v 会从内存中丢失是有意义的。但是它就是这样啊。似乎是语言的缺陷。在 C 语言中,我认为 $v 将超出循环范围。
  • @jamador PHP 手册建议在第一个 foreach 循环之后调用 unset($item); 以避免此问题。 php.net/manual/en/control-structures.foreach.php
【解决方案3】:

第一个循环

$v = $a[0];
$v = $a[1];
$v = $a[2];
$v = $a[3];

是的!当前$v = $a[3] 位置。

第二个循环

$a[3] = $v = $a[0], echo $v; // same as $a[3] and $a[0] == 'zero'
$a[3] = $v = $a[1], echo $v; // same as $a[3] and $a[1] == 'one'
$a[3] = $v = $a[2], echo $v; // same as $a[3] and $a[2] == 'two'
$a[3] = $v = $a[3], echo $v; // same as $a[3] and $a[3] == 'two'

因为$a[3]是在处理之前分配的。

【讨论】:

    【解决方案4】:

    我只是偶然来到这里,OP 的问题引起了我的注意。不幸的是,我不明白上面的任何解释。在我看来,每个人都知道,明白,接受,只是无法解释。

    幸运的是,foreach 上的 PHP 文档中的一句话说明了这一点:

    警告:即使在 foreach 循环之后,$value 的引用和最后一个数组元素仍然存在。建议通过 unset() 销毁。

    【讨论】:

    • 我也在考虑同样的事情,为什么 foreach 中的局部变量甚至会打扰。谢谢
    • 这种事情让 PHP 变得更糟了。我认为这种行为会诱使开发人员在他们的代码中引入错误,Zend 应该改变这种行为。
    【解决方案5】:

    我认为这段代码显示的过程更清晰。

    <?php
    
    $a = array ('zero','one','two', 'three');
    
    foreach ($a as &$v) {
    }
    
    var_dump($a);
    
    foreach ($a as $v) {
      var_dump($a);
    }
    

    结果: (注意最后两个数组)

    array(4) {
      [0]=>
      string(4) "zero"
      [1]=>
      string(3) "one"
      [2]=>
      string(3) "two"
      [3]=>
      &string(5) "three"
    }
    array(4) {
      [0]=>
      string(4) "zero"
      [1]=>
      string(3) "one"
      [2]=>
      string(3) "two"
      [3]=>
      &string(4) "zero"
    }
    array(4) {
      [0]=>
      string(4) "zero"
      [1]=>
      string(3) "one"
      [2]=>
      string(3) "two"
      [3]=>
      &string(3) "one"
    }
    array(4) {
      [0]=>
      string(4) "zero"
      [1]=>
      string(3) "one"
      [2]=>
      string(3) "two"
      [3]=>
      &string(3) "two"
    }
    array(4) {
      [0]=>
      string(4) "zero"
      [1]=>
      string(3) "one"
      [2]=>
      string(3) "two"
      [3]=>
      &string(3) "two"
    }
    

    【讨论】:

      【解决方案6】:

      这个问题提供了很多解释,但没有明确的例子说明如何解决这种行为导致的问题。在大多数情况下,您可能希望通过引用 foreach 在您的传递中包含以下代码。

      foreach ($array as &$row) {
          // Do stuff
      }
      // Unset to remove the reference
      unset($row);
      

      【讨论】:

      • 对我不起作用,必须改用通常的unset($row[$k]);。
      • 我认为 unset 应该放在 foreach 循环之后..
      【解决方案7】:

      这个:

      $a = array ('zero','one','two', 'three');
      
      foreach ($a as &$v) {
      
      }
      
      foreach ($a as $v) {
          echo $v.PHP_EOL;
      }
      

      和

      一样
      $a = array ('zero','one','two', 'three');
      
      $v = &$a[3];
      
      for ($i = 0; $i < 4; $i++) {
          $v = $a[$i];
          echo $v.PHP_EOL; 
      }
      

      或

      $a = array ('zero','one','two', 'three');
      
      for ($i = 0; $i < 4; $i++) {
          $a[3] = $a[$i];
          echo $a[3].PHP_EOL; 
      }
      

      或

      $a = array ('zero','one','two', 'three');
      
      $a[3] = $a[0];
      echo $a[3].PHP_EOL;
      
      $a[3] = $a[1]; 
      echo $a[3].PHP_EOL;
      
      $a[3] = $a[2];
      echo $a[3].PHP_EOL;
      
      $a[3] = $a[3]; 
      echo $a[3].PHP_EOL;
      

      【讨论】:

        【解决方案8】:

        我发现这个例子也很棘手。为什么在最后一次迭代的第二个循环中什么都没有发生($v 保持“两个”),是 $v 指向 $a[3](反之亦然),所以它不能给自己赋值,所以它保持以前的赋值:)

        【讨论】:

          【解决方案9】:

          因为如果您创建对变量的引用,该变量的所有名称(包括原始名称)都会成为引用。

          【讨论】:

          • 如果是这种情况,那么为什么在第二个循环中将 $v 更改为 &$v 会改变结果,即它回显零、一、二、三?
          • 因为在第二个循环中使用 &$v 时,您正在更改变量的引用。这样,问题就解决了。
          猜你喜欢
          • 2012-01-03
          • 2015-07-11
          • 1970-01-01
          • 1970-01-01
          • 2011-09-18
          • 2014-12-23
          • 2017-08-12
          相关资源
          最近更新 更多