PHP Pass по ссылке в foreach

У меня есть этот код:

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

foreach ($a as &$v) {

}

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

Может кто-нибудь объяснить, почему вывод: нуль один два два.

Из руководства по сертификации сертификации zend.

Ответ 1

Так как во втором цикле $v по-прежнему является ссылкой на последний элемент массива, он каждый раз перезаписывается.

Вы можете видеть это так:

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

foreach ($a as &$v) {

}

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

Как вы можете видеть, последний элемент массива принимает текущее значение цикла: "ноль", "один", "два", а затем он просто "два"...:)

Ответ 2

Мне пришлось потратить несколько часов, чтобы понять, почему [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 присваивается значение 'two'. Так как $v указывает на $a[3], $a[3] теперь получает значение "два". Имейте это в виду.

В последней итерации $v (которая указывает на $a[3]) теперь имеет значение 'two', потому что $a[3] было установлено на два в предыдущей итерации. two. Это объясняет, почему "два" повторяются, когда $v печатается на последней итерации.

Ответ 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

Я думаю, что этот код показывает процедуру более понятной.

<?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"
}

Ответ 5

Я попал сюда случайно, и вопрос OP привлек мое внимание. К сожалению, я не понимаю ни одного из объяснений сверху. Мне кажется, что все это знают, понимают, понимают, просто не могут объяснить.

К счастью, чистое предложение из документации PHP на foreach делает это совершенно ясным:

Предупреждение: Ссылка на $value и последний элемент массива остаются даже после цикла foreach. Рекомендуется уничтожить его unset().

Ответ 6

В этом вопросе есть много объяснений, но нет четких примеров того, как решить проблему, вызванную этим поведением. В большинстве случаев вам, вероятно, понадобится следующий код в вашем проходе по ссылке foreach.

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

Ответ 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

Потому что, если вы создаете ссылку на переменную, все имена для этой переменной (включая оригинал) СТАТЬ СПРАВОЧНИКАМИ.