PHP数组中的指针小思考

使用each()函数调用函数reset()将数组指针重置
服务器君一共花费了225.577 ms进行了5次数据库查询,努力地为您提供了这个页面。
试试阅读模式?希望听取您的建议

最近在工作中遇到一个很奇怪的问题,在使用each这个函数对一个数组遍历之后,再把该数组作为实参传递给一个函数,在函数内部再次使用each遍历形参数组。说明一下,两次使用each这个函数的目的很简单,就是将数组中的key转换成变量名,而key对应的value值转换成变量的值。其实这个功使用函数extract就能实现。在来接着说说在函数内部使用each函数时出现了什么问题,在遍历完之后发现部分变量为NULL,也就是说丢失了部分变量。不知道描述清楚没有,还是将工作中问题简化一下,用下面代码说明一下吧。

<?php
	$arr = array('var1'=>1,'var2'=>2,'var3'=>3,'var4'=>4,'var5'=>5);
	while( list($key,$value) = each($arr) )
	{
		$$key = $value;
	}
	//下面这一步操作很关键,问题就出在这里,遍历完之后,对数组添加一个值。
	$arr['var6'] = 6;
			
	func($arr);
        
	function func($arrtmp)
	{
  		while(list($key,$value) = each($arrtmp) )
    	{
   			$$key = $value;
    	}
   		var_dump($var1);
     	var_dump($var2);
    	var_dump($var3);
     	var_dump($var4);
      	var_dump($var5);
     	var_dump($var6);
	}
?>

输出结果:NULL NULL NULL NULL NULL int(6).

按照常规的想法,此时在函数func内部变量 $var1,$var2,$var3,$var4,$var5,$var6 都应该有了,但是事实并非于此,只有变量$var6有值,而其他几个变量都是NULL。这是为什么呢?

问题就出在我们今天要讨论的数组指针的问题上。each这个函数会将当前数组指针指向的元素以数组的形式返回,并将数组指针向前移动一位,指向下一个数组 单元。在我们使用each函数将数组 $arr 遍历完之后,$arr数组的内部指针已经指向了最后一个单元的下一位(没有任何值)。就在此时我们又 执行了 $arr['var6'] = 6 这个操作,为数组添加了一个新的单元,我们知道,数组在内存中存储肯定都是连续的地址单元。也就是说$arr['var6']的值在内存中的位置就应该在 当前数组指针指向的那个单元(之前为空)。而且给数组赋值是不会移动数组内部指针的,在赋值完成之后,$arr 数组的数组指针就由原来指向一个NULL变成指向一个有实际值存在的地址单元了。

对于数组作为参数在函数间传递时又存在这样一个规律:我们知道,函数调用时,系统会将实参copy一份赋值给形参(引用调用除外),而对于数组,不仅仅 copy了实参的值,而且还copy了实参数组当前内部指针的位置。如果实参内部指针的位置指向了数组末尾,那么系统会将形参的内部指针重置,指向形参数 组的第一个单元;如果实参内部指针的位置不在数组末尾,即指向了有效的单元,那么系统会将形参的数组指针位置与实参的数组指针指向值相同的数组单元。

如果不做 $arr['var6'] = 6 这一步操作,6个变量($var1-$var6)都将有值,因为在each之后,数组指针已经指向了数组的末尾,那么在调用函数 func()时,系统会重置 $arrtmp的数组指针,将其指向第一个元素。但是在进行 $arr['var6'] = 6这步操作之后,一切就改变了,这一个操作让$arr的数组指针由原来指向一个NULL变成了一个有效值(说明一下,赋值前后,数组指针指向的地址单元一 直是没有变化的,只不过是赋值前,那个地址单元什么也没有,而赋值之后变成了6)。这就使得$arr的数组指针指向了一个有效的单元,那么在调用函数 func()时,系统是不会重置$arrtmp的数组指针的,$arrtmp的数组指针将会跟$arr的数组指针一样,指向他自己的最后一个单元。而 each函数又是从当前数组指针的位置开始工作的。因此each函数操作的第一个结果的返回值就是数组$arrtmp的最后一个元素了,它将数组指针再向下移动一位,while循环到此结束,因此$arrtmp['var1']-$arrtmp['var5']都没有被遍历到,最终导致$var1-$var6为NULL。

数组在赋值的过程中,赋值数组和被赋值数组各自数组指针的变化情况。 先给出一条结论,然后我们在用代码来证明这个结论吧。$arrtmp=$arr;在这个赋值表达式中我把$arr叫做赋值数组,把$arrtmp叫做被赋值数组。数 组在赋值时,如果赋值数组的数组指针已经指向了数组末尾,则赋值之后赋值数组的数组指针会被重置,指向数组第一个元素;如果在赋值时,赋值数组的数组指针 没有指向数组末尾,而是指向了任何一个有效的数组元素,那么在赋值之后赋值数组的数组指针是不会被重置的,而是保留其原来指向的元素。在赋值之后,被赋值 数组不仅有了赋值数组的值,而且赋值数组的数组指针指向了那个元素,被赋值的数组也会指向自己中值相同的那个元素。

demo1:

<?php
	$arr = array('var1'=>1,'var2'=>2,'var3'=>3,'var4'=>4,'var5'=>5);
	while( list($key,$value) = each($arr) )
	{
		if($value == 4) break;
	}
	var_dump(current($arr));
	
	$arr1 = $arr;
	
	var_dump(current($arr));
	var_dump(current($arr1));
?>

demo1 的执行结果是:int(5) int(5) int(5) 。从这个结果可以看出,赋值前后$arr的数组指针位置没有发生任何变化,$arr1不仅值跟$arr相同,而且数组指针所指向的元素值也是相同的。现在 用上述结论来解释这个结果,在while循环中,有一个if判断语句,目的是不让$arr的数组指针指向数组末尾,而是保留在一个有效的位置。 在$value=4时会跳出循环,而each这个函数会将数组指针向前移动一位,这就导致了$arr的数组指针指向了第5个元素,所以在赋值之 前,current($arr)的结果是5,赋值之后,由于在赋值之前$arr的当前指针并没有指向末尾,因此在赋值之后不会将$arr的数组指针进行重 置,而是保留了其原有的位置,因此在赋值之后使用current($arr)的结果仍然是5。赋值时$arr1不仅获得了$arr的值,而且数组指针指向 的元素和$arr的相同,二者都是5。

demo2:

<?php
$arr = array('var1'=>1,'var2'=>2,'var3'=>3,'var4'=>4,'var5'=>5);
while( list($key,$value) = each($arr) )
{
    //if($value == 4) break;
}
var_dump(current($arr));
$arr1 = $arr;
var_dump(current($arr));
var_dump(current($arr1));
?>

demo2中我们将 if($value == 4) break; 这一句注释掉了,目的很简单,就是通过each将$arr的数组指针位置指向数组末尾。

demo2 的执行结果:bool(false) int(1) bool(false) 。如果数组指针对应的元素为0,"",或者不是一个有效的值时,current函数会返回false,$arr的值中没有为0或者""的情况,因此可以断 定是因为数组指针指向了一个无效的元素而导致current返回了一个false。换句话说就是可以确定在while循环完成之后,$arr的数组指针已 经指向了数组的末尾。所以我们看到在赋值之前current($arr)的值是false,而赋值之后current($arr)的值变成了1,说明赋值 之后$arr的数组指针被重置了,指向了数组的第一个元素。current($arr1)的值为false,说明赋值之后$arr1让然保留了赋值之 前$arr的数组指针指向的元素。

通过demo1和demo2就可以证明上述结论了。

因此为了在遍历数组时不受数组指针的影响,最好在使用each()函数之前或者之后调用函数reset()将数组指针重置。这样就可以避免上述问题的发生了。另外还有一个操作数组指针的函数prev(),它的作用是将数组指针当前的位置后退一位,它也需要注意一点,就是如果数组指针已经指向数组末尾,那么使它就得不到想要的结果了。

顺便说一下foreach这个函数,使用foreach函数来遍历数组时,它会重置数组指针,将其指向数组的第一个元素。必须注意的是foreach操作的对象是对你要遍历的数组的copy值,而不是遍历数组本身。

本文地址:http://www.nowamagic.net/librarys/veda/detail/1552,欢迎访问原出处。

不打个分吗?

转载随意,但请带上本文地址:

http://www.nowamagic.net/librarys/veda/detail/1552

如果你认为这篇文章值得更多人阅读,欢迎使用下面的分享功能。
小提示:您可以按快捷键 Ctrl + D,或点此 加入收藏

大家都在看

阅读一百本计算机著作吧,少年

很多人觉得自己技术进步很慢,学习效率低,我觉得一个重要原因是看的书少了。多少是多呢?起码得看3、4、5、6米吧。给个具体的数量,那就100本书吧。很多人知识结构不好而且不系统,因为在特定领域有一个足够量的知识量+足够良好的知识结构,系统化以后就足以应对大量未曾遇到过的问题。

奉劝自学者:构建特定领域的知识结构体系的路径中再也没有比学习该专业的专业课程更好的了。如果我的知识结构体系足以囊括面试官的大部分甚至吞并他的知识结构体系的话,读到他言语中的一个词我们就已经知道他要表达什么,我们可以让他坐“上位”毕竟他是面试官,但是在知识结构体系以及心理上我们就居高临下。

所以,阅读一百本计算机著作吧,少年!

《PHP经典实例(第2版)》 斯克拉(David Sklar) (作者), 切贝特伯格(Adam Tracbtenberg) (作者), 李松峰 (译者), 秦绪文 (译者), 李丽 (译者)

PHP经典实例(第2版)能够为您节省宝贵的Web开发时间。有了这些针对真实问题的解决方案放在手边,大多数编程难题都会迎刃而解。《PHP经典实例(第2版)》将PHP的特性与经典实例丛书的独特形式组合到一起,足以帮您成功地构建跨浏览器的Web应用程序。在这个修订版中,您可以更加方便地找到各种编程问题的解决方案,《PHP经典实例(第2版)》中内容涵盖了:表单处理;Session管理;数据库交互;使用Web服务。

更多计算机宝库...