PHP Передача по ссылке в foreach

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

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

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

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

Solutions Collecting From Web of "PHP Передача по ссылке в foreach"

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

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

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

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

Мне пришлось потратить несколько часов, чтобы понять, почему [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 печатается на последней итерации.

Первый цикл

 $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] назначается.

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

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

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

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

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

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

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

Эта :

 $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; 

Я нашел этот пример также сложным. Почему во втором цикле на последней итерации ничего не происходит ($ v остается «два»), это то, что $ v указывает на $ a [3] (и наоборот), поэтому он не может присваивать значение самому себе, поэтому он сохраняет предыдущее присвоенное значение 🙂

Потому что, если вы создаете ссылку на переменную, все имена этой переменной (включая оригинал) ставят ссылки.