Цикл foreach и ссылка & $value
Почему пустой цикл foreach может изменить результат.
У меня есть следующий код:
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value ++;
var_dump($variable);
В результате получается
array (size=4)
0 => int 2
1 => int 3
2 => int 4
3 => &int 5
Теперь, когда я добавляю пустой цикл foreach, подобный этому
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value ++;
foreach ($variable as $key => $value);
var_dump($variable);
Я получаю это:
array (size=4)
0 => int 2
1 => int 3
2 => int 4
3 => &int 4
может кто-нибудь объяснить мне, почему последний элемент не изменяется, когда я добавляю второй пустой цикл, и почему существует и перед последним элементом?
Ответы
Ответ 1
В конце первого цикла $value
указывает на то же место, что и $variable[3]
(они указывают на то же место в памяти):
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value ++;
Даже когда этот цикл закончен, $value
по-прежнему является ссылкой, указывающей на то же место в памяти, что и $variable[3]
, поэтому каждый раз, когда вы сохраняете значение в $value
, это также перезаписывает значение, сохраненное для $variable[3]
:
foreach ($variable as $key => $value);
var_dump($variable);
При каждой оценке этого foreach оба $value
и $variable[3]
становятся равными значению итерабельного элемента в переменной $.
Итак, в третьей итерации второго цикла $value
и $variable[3]
становятся равными 4 по ссылке, то в течение 4-й и последней итерации второго цикла ничего не меняется, потому что вы передаете значение $variable[3]
(который все еще &$value
) до $value
(который все еще &$value
).
Это очень сбивает с толку, но это даже не очень своеобразно; это код, выполняемый точно так, как должен.
Дополнительная информация здесь: PHP: переход по ссылке
Ответ 2
Это столкновение имен: имя $value, введенное в первом цикле, существует после него и используется во втором цикле. Таким образом, все присваивания ему фактически присваиваются исходному массиву. То, что вы сделали, легче наблюдать в этом коде:
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value ++;
$value = 123; // <= here you alter the array!
var_dump($variable);
и вы увидите $variable[3]
как 123
.
Один из способов избежать этого, как говорили другие, - unset ($value)
после цикла, что должно быть хорошей практикой, как рекомендовано в руководстве. Другой способ - использовать другую переменную во втором цикле:
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value ++;
foreach ($variable as $key => $val);
var_dump($variable);
который не изменяет ваш массив.
Ответ 3
Последний элемент массива будет remian даже после цикла foreach. Поэтому необходимо использовать функцию unset
вне цикла. Это
$variable = [1,2,3,4];
foreach ($variable as $key => &$value) {
$value++;
}
unset($value);
var_dump($variable);
Ссылка на руководство находится здесь http://php.net/manual/en/control-structures.foreach.php
Ответ 4
Как phil
указано в комментариях:
Как указано в руководстве, после использования следует отключить() ссылки.
$variable = [1,2,3,4];
foreach ($variable as $key => &$value) {
$value ++;
}
unset($value);
foreach ($variable as $key => $value);
print_r($variable);
Вернется:
Array
(
[0] => 2
[1] => 3
[2] => 4
[3] => 5
)
Пример
Объяснение
Взято из руководства foreach()
. (Смотрите большой красный ящик)
Ссылка на значение $и последний элемент массива остаются даже после foreach loop. Рекомендуется уничтожить его unset().
В основном это означает: что ссылочное значение &$value
и последний элемент/элемент в массиве, который в этом случае равен 4
, остаются неизменными. Чтобы противодействовать этой проблеме, вы должны будете unset()
значение после использования, иначе оно останется в массиве в качестве исходного значения (если это имеет смысл).
Вы также должны прочитать следующее: Как работает PHP foreach?
Ответ 5
После цикла вы должны отключить эту ссылку, используя:
unset($value);
Таким образом, весь ваш код должен работать следующим образом:
$variable = [1,2,3,4];
foreach ($variable as $key => &$value) {
$value++;
}
unset($value);
var_dump($variable);
Нет смысла ставить unset($value);
внутри цикла
Объяснение - после цикла значение $по-прежнему устанавливается в последний элемент массива, поэтому вы можете использовать его после цикла $value = 10;
(перед отключением), и вы увидите, что последний элемент массива был изменен на 10
. Кажется, что var_dump
хочет немного помочь нам в этом случае и показывает, что есть ссылка для последнего элемента, и, конечно, когда мы используем unset
, у нас есть желаемый вывод var_dump
.
Вы также можете посмотреть следующий script:
<?php
$array = [1, 2, 3, 4];
var_dump($array);
$x = &$array[2];
var_dump($array);
$x += 20;
unset($x);
var_dump($array);
?>
Мы не используем здесь цикл, и если для ссылки задан элемент массива, var_dump
показывает нам это помещение &
перед типом этого элемента.
Однако, если приведенный выше код мы изменили ссылку и установили его таким образом $x = &$array;
var_dump не показывал нам никакой ссылки.
Также в следующем коде:
<?php
$x = 23;
$ref = &$x;
var_dump($x);
?>
var_dump()
не даст нам никакой подсказки.
Ответ 6
Обязательное утверждение: ссылки злы!
Выполнение кода:
$variable = [1,2,3,4];
foreach ($variable as $key => &$value)
$value++;
После завершения цикла; $value
является ссылкой на $variable[3]
и, следовательно, имеет значение int(4)
.
foreach ($variable as $key => $value);
На каждой итерации $variable[3]
получает элемент $variable[<k>]
, где 0 <= k < 3
. На последней итерации ему присваивается собственное значение, которое соответствует предыдущей итерации, поэтому оно int(4)
.
Отключение $value
между двумя контурами разрешает ситуацию. См. Также более ранний ответ.