1. ホーム
  2. php

[解決済み] PHP Foreach 参照渡し。最後の要素が重複している?(バグ?) [重複]

2022-04-25 09:59:54

質問

私が書いていた簡単なphpスクリプトで、とても奇妙な動作がありました。バグを再現するために必要最小限のものに縮小しました。

<?php

$arr = array("foo",
             "bar",
             "baz");

foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);

foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?

?>

これが出力されます。

Array
(
    [0] => foo
    [1] => bar
    [2] => baz
)
Array
(
    [0] => foo
    [1] => bar
    [2] => bar
)

これはバグなのでしょうか、それとも本当に不思議な動作が起こるはずなのでしょうか?

解決方法は?

最初のforeachループの後。 $item は、まだ何らかの値への参照であり、その値もまた $arr[2] . そのため、参照による呼び出しを行わない 2 番目のループの各 foreach 呼び出しは、その値を置き換えることになり、その結果 $arr[2] を新しい値で置き換えます。

つまりループ1では、値と $arr[2] になります。 $arr[0] であり、'foo'である。

ループ2、値や $arr[2] になります。 $arr[1] であり、'bar'である。

ループ3では、値や $arr[2] になります。 $arr[2] となり、'bar' となる(ループ2があるため)。

値'baz'は、実際には2番目のforeachループの最初の呼び出しで失われます。

出力のデバッグ

ループの各反復に対して $item を再帰的に表示し、さらに配列 $arr .

最初のループを通すと、次のような出力が得られます。

foo
Array ( [0] => foo [1] => bar [2] => baz )

bar
Array ( [0] => foo [1] => bar [2] => baz )

baz
Array ( [0] => foo [1] => bar [2] => baz )

ループの終わりで $item と同じ場所を指しています。 $arr[2] .

2つ目のループを通すと、このような出力が得られます。

foo
Array ( [0] => foo [1] => bar [2] => foo )

bar
Array ( [0] => foo [1] => bar [2] => bar )

bar
Array ( [0] => foo [1] => bar [2] => bar )

に新しい値を入れるたびに、配列がどのように変化するかがわかると思います。 $item を更新し、さらに $arr[3] というのは、どちらも同じ場所を指しているからです。ループが配列の 3 番目の値に到達したとき、そこには bar なぜなら、このループの前の繰り返しで設定されたばかりだからです。

バグなのか?

いいえ、これは参照されたアイテムの動作であり、バグではありません。というようなことを実行しているのと同じようなものでしょう。

for ($i = 0; $i < count($arr); $i++) { $item = $arr[$i]; }

foreachループは、参照されている項目を無視できるという点では、特別な性質はありません。ループの外で行うように、その都度その変数に新しい値をセットするだけです。