PHP在foreach中通过引用传递

Cen*_*ion 177 php

我有这个代码:

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

foreach ($a as &$v) {

}

foreach ($a as $v) {
  echo $v.PHP_EOL;
}
Run Code Online (Sandbox Code Playgroud)

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

来自zend认证学习指南.

M S*_*M S 159

我不得不花费几个小时来弄清楚为什么[3]每次迭代都在改变.这是我到达的解释.

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

例如在

$a = array('zero', 'one', 'two', 'three');
Run Code Online (Sandbox Code Playgroud)

如果我们这样做

$v = &$a[0]
Run Code Online (Sandbox Code Playgroud)

第0个元素($a[0])成为引用变量. $v指向那个变量; 因此,如果我们做出任何改变$v,它将反映出来,$a[0]反之亦然.

现在如果我们这样做

$v = &$a[1]
Run Code Online (Sandbox Code Playgroud)

$a[1]将成为一个参考变量,$a[0]并将成为一个正常变量(因为没有其他人指向$a[0]它转换为正常变量.当没有其他人指向它时,PHP足够聪明,使其成为正常变量)

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

foreach ($a as &$v) {

}
Run Code Online (Sandbox Code Playgroud)

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

因为$v指向$a[3]任何更改$v导致更改为$a[3]

在第二个循环中,

foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
Run Code Online (Sandbox Code Playgroud)

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

在最后一次迭代之前的迭代中,$v赋值为'two'.既然$v指向$a[3],$a[3]现在获得值'二'.记住这一点.

在最后一次迭代中,$v(指向$a[3])现在具有值'two',因为$a[3]在上一次迭代中设置为2.two打印出来.这解释了为什么在最后一次迭代中打印$ v时会重复"两个".

  • 我喜欢你的解释,但这仍然是让我不断混淆的事情之一.我必须记住要理解_stay_理智.:-) (3认同)
  • 对我来说,“引用变量”就是“引用”,在这个例子中是“$v”。另一方面,`$a` 始终是一个普通变量,在我看来不是一个引用变量。因此,PHP 也不是“聪明的”,因为它知道一个变量没有对它的引用,因为如果在某处引用了变量本身,它实际上并不重要。需要理解的重要一点是`$v` 是一个引用而不是一个变量。否则很好的解释。 (2认同)
  • 你说“$a[1]将成为引用变量,$a[0]将成为普通变量”;这实际上并不正确:从技术上讲,每个变量都是对内存空间的引用;$v = &$a[1] 表示 $v 和 $a[1] 将引用同一内存空间; (2认同)
  • 很好的解释!这是由于变量的作用域不限于循环,因此引用“$v”从第一个循环中泄漏。对于有意的代码混淆来说,这将是一个非常好的技巧...像往常一样,修复方法是“unset($v)”以删除引用或将该代码移动到新函数中,以便 $v 获得自己的作用域并且不会泄漏到以下代码。 (2认同)

Mac*_*ade 136

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

你可以这样看:

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

foreach ($a as &$v) {

}

foreach ($a as $v) {
  echo $v.'-'.$a[3].PHP_EOL;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 在改进输出后我能够理解:`$ v得到项[0](零).$ a [3]现在为零$ v获得项目[1](一).$ a [3]现在是一个$ v得到项目[2](两个).$ a [3]现在是两个$ v得到项目[3](两个).$ a [3]现在是两个` (10认同)
  • 它不会停止.最后一个数组项分配有当前循环值.所以它被分配为"零",然后是"一个",然后是"两个".在最后一次迭代中,由于前一次迭代,它被赋予了自己的值,即'2'.所以它只是"两个". (4认同)
  • 这是非常直观的.对我来说,当循环离开时,$ v会丢失内存.但是它就是这样啊.似乎是语言中的一个缺陷.在C中,我认为$ v将超出循环范围. (3认同)
  • @jamador PHP 手册建议在第一个 foreach 循环之后调用 `unset($item);` 以避免这个问题。http://php.net/manual/en/control-structs.foreach.php (2认同)

Tae*_*aeL 50

第一个循环

$v = $a[0];
$v = $a[1];
$v = $a[2];
$v = $a[3];
Run Code Online (Sandbox Code Playgroud)

是! 当前$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'
Run Code Online (Sandbox Code Playgroud)

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


sha*_*yyx 16

我偶然来到这里,OP的问题引起了我的注意.不幸的是,我不理解顶部的任何解释.对我来说似乎每个人都知道它,得到它,接受它,只是无法解释.

幸运的是,来自foreach的 PHP文档的纯粹句子使得这一点完全清楚:

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


Cha*_*ang 15

我认为这段代码表明程序更加清晰.

<?php

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

foreach ($a as &$v) {
}

var_dump($a);

foreach ($a as $v) {
  var_dump($a);
}
Run Code Online (Sandbox Code Playgroud)

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

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"
}
Run Code Online (Sandbox Code Playgroud)


Goo*_*ose 10

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

foreach ($array as &$row) {
    // Do stuff
    // Unset
    unset($row);
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为未设置的应该放在foreach循环之后。 (3认同)

TTT*_*TTT 5

这个 :

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

foreach ($a as &$v) {

}

foreach ($a as $v) {
    echo $v.PHP_EOL;
}
Run Code Online (Sandbox Code Playgroud)

是相同的

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

$v = &$a[3];

for ($i = 0; $i < 4; $i++) {
    $v = $a[$i];
    echo $v.PHP_EOL; 
}
Run Code Online (Sandbox Code Playgroud)

或者

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

for ($i = 0; $i < 4; $i++) {
    $a[3] = $a[$i];
    echo $a[3].PHP_EOL; 
}
Run Code Online (Sandbox Code Playgroud)

或者

$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;
Run Code Online (Sandbox Code Playgroud)