PHP关于foreach的问题

名字不能超过十个字 发布于 2013/01/15 16:58
阅读 2K+
收藏 5
PHP
<?php
$a = array('abe','ben','cam');
foreach ($a as $k=>&$n)
    $n = strtoupper($n);
foreach ($a as $k=>$n) // notice NO reference here!
    echo "$n\n";
print_r($a);
?>

will result in:

ABE
BEN
BEN
Array
(
    [0] => ABE
    [1] => BEN
    [2] => BEN
)
手册上的一个warning
Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().

大神们来说说这里最后一个元素$n对后面个foreach的影响,怎么改变值的,很不理解啊





加载中
0
西门掃雪
西门掃雪

这个问题很简单,作用域造成的!


这个问题很好,当面试题用, 能别人坑死

名字不能超过十个字
名字不能超过十个字
头像略犀利。。。
0
Just-Code
Just-Code

把程序改成这样就有点明白了:

$a = array('abe','ben','cam');
foreach ($a as $k=>&$n)
    $n = strtoupper($n);
foreach ($a as $k=>$n) // notice NO reference here!
    print_r($a);

Array
(
    [0] => ABE
    [1] => BEN
    [2] => ABE
)
Array
(
    [0] => ABE
    [1] => BEN
    [2] => BEN
)
Array
(
    [0] => ABE
    [1] => BEN
    [2] => BEN
)

因为没有unset($n),所以它始终指向数组的最后一个元素,第二个foreach里第一次循环把$n,也就是$a[2]改成了ABE,第二次循环改成了BEN,第三次就也是BEN了。

 

0
hello_zhong
hello_zhong

你在第二个 没加引用的foreach 里面输出数组就明白了。

第一次循环
&$n的值已经是一个引用了。
第二次开始循环,&n第一次变成了abc第二次变成了ben,第三次就是引用自己。就是上一个被赋值的对象ben咯。

只要用过个名字就没事儿了的

0
joenali
joenali

第一次foreach后$n指向的是$a[2],因此第二个foreach可以改写为

foreach ($a as $k=>$a[2]) { // notice NO reference here!
    echo "$n\n";
}

foreach内部执行:

$a[2] = $a[0]  //$a[2] = ‘ABE’
$a[2] = $a[1]  //$a[2] = ‘BEN’
$a[2] = $a[2] //$a[2] = ‘BEN’


名字不能超过十个字
名字不能超过十个字
原来如此,明白了
0
放牛娃1988
放牛娃1988

楼上是正解,foreach这个&的作用是别名,所以第一次循环过后,$n已经是$a[2]的别名了。所以第二个循环开始后,每次对$n的赋值,都是对$a[2]的赋值。所以$a[2]的值将会依次变成$a[0],$a[1],最后一次是自己给自己赋值,没影响,还是$a[1],最终数组就变成了$a[0],$a[1],$a[1]。

曾经被坑了,以为是PHP的bug的飘过。php这个foreach结构的引用设计不好,应该在每次foreach时使用别名的时候,结束foreach时就应该自动unset掉,保留这个link没啥用处,反而bug多多。

Narky
Narky
主要是因为php这个玩意不会对代码做任何优化工作,都交给coder去完成
0
放牛娃1988
放牛娃1988
看楼上的一众回复,看来被坑的人不少
名字不能超过十个字
名字不能超过十个字
就是啊,被坑了。以前一直没注意这个
0
Liuxd
Liuxd

明显作用域问题。

代码不是这么写的,代码是造福人类滴,不是坑爹滴

0
fly020212
fly020212

不算是作用域的问题,PHP 的变量作用域还没有小到在一个foreach {} 或者 if {} else {} 内

上面很多人都解释清楚了,foreach 后 $n 被引用到了 数组的最后一个元素,如果程序后面的代码使用到了 $n ,对其进行了赋值,就会改变 之前 foreach 的那个数组,改变最后一个元素的值,如:

<?php
$a = array('abe','ben','cam');
// array to upcase
foreach ($a as $k=>&$n)
    $n = strtoupper($n);
print_r($a);

// do someting else
// now we use a temp var named $n
$n = 'some else';
print_r($a);

?>

// print 1
Array
(
    [0] => ABE
    [1] => BEN
    [2] => CAM
)

// print 2
Array
(
    [0] => ABE
    [1] => BEN
    [2] => some else   // have been changed
)
当然,如果后续代码没有使用 $n 的话,退出 函数块 是没有什么影响的,但这里就留下了一个隐患,所以每次 对 数组 foreach 使用引用修改值时,紧接着要 unset 这个引用。


Eddy8
Eddy8
正解~
放牛娃1988
放牛娃1988
同意。什么叫作用域的问题?跟作用域相关的问题就是作用域问题?那我还说是变量的问题呢。既然人家诚心诚意求解释,就解释得清楚些。PHP的作用域就是全局和局部,这里只有局部,根本没有全局,何来作用域问题一说?说是作用域问题的,都是被C++/JAVA的块作用域和头部作用域相提并论的结果。
0
朱__朱
朱__朱
$a = array('abe','ben','cam');
foreach ($a as $k=>&$n){
	$n = strtoupper($n);
}
foreach ($a as $k=>$n) {
	//由于$n在上一次遍历时已经被定义为了引用,所以这里虽然你没有声明引用,但$n变量仍然为一个引用,它指向的目标就是上一次遍历时最后的那个元素,因此,本次遍历时,其实每次都是在修改$a的最后一个元素而已。

	//第一次将$n修改为了$a[0];
	//ABE,BEN,ABE
	//第二次将$n修改为了$a[1];
	//ABE,BEN,BEN
	//第三次将$n修改为了$a[2];
	//ABE,BEN,BEN
	echo "$n\n";
}
print_r($a);
编程有个格言:可以这么写,不代表应该这么写(除非你真的不介意时不时冒出个摸不着头的问题)。
0
名字不能超过十个字
名字不能超过十个字
多谢各位,明白了
返回顶部
顶部