最近更新: 2007-07-16

foreach 時使用 reference 的陷阱

前幾天在重構一段 PHP 程式碼時,不小心踩到 foreach 使用「參照(reference)」的陷阱。上網查了一下,似乎踩到的人還不少。我想了一下,把原因換成另一段程式碼形式說明,再說明解法。基本上,這是個 trick ,而不是 bug 。

一段很平常的迴圈敘述,差別僅在第一個迴圈使用參照(PHP5 or later),第二個不是。

<?php
$a = array(1,2,3);

foreach ($a as &$v) {
    echo $v,"\n";
}

echo "----\n";

foreach ($a as $v) {
    echo $v,"\n";
}
?>

預期輸出結果。

1
2
3
----
1
2
3

實際輸出結果。

1
2
3
----
1
2
2

因為 $v 在第一個迴圈時,被設定為一個參照。當迴圈結束後,$v 正好參照最後一個陣列元素 $a[2] 。而接下來的迴圈,就成為參照的指派動作,把每個一陣列元素的值都透過參照 $v 指派為最後一個陣列元素 $a[2] 了。每次輸出的結果實際上都是 $a[2] 的現值。第二個迴圈的動作等同下列所示:

<?php
$a = array(1,2,3);

foreach ($a as &$v) {
    echo $v,"\n";
}
echo "----\n";
/*
$v is still reference to $a[2],
so this iterator will like following:
*/
reset($a);
while( $t = current($a) ) {
    $v = $t; //$v ref $a[2], so $a[2] will be $t
    echo $v,"\n";
    next($a);
}
?>

解決這個問題的方式是,在使用參照的迴圈結果後,加上一個 unset 的動作消除那個參照變數。如下所示。

<?php
$a = array(1,2,3);

foreach ($a as &$v) {
    echo $v,"\n";
}
unset($v);
?>

按 PHP 目前的特性,這是個好習慣。

樂多舊網址: http://blog.roodo.com/rocksaying/archives/3669955.html

樂多舊回應
未留名 (#comment-15226463)
Tue, 11 Dec 2007 12:01:34 +0800
這種寫法叫做畫蛇添足, 曲解基本觀念所致.