3.1 变量引用
首先看一道题目:
代码如下:(源码文件:ch03/reference_1.php)
$a = 1; $b = & $a; $b = 2; var_dump($a);
这时$a的输出结果是什么?
答案为2。
除第1行为$a赋值外,没有再对$a有任何操作,但$a的值由1变为2。
这就是本节要讨论的变量引用。
3.1.1 指针与引用
在C语言里,指针是一个强大的存在,它可以通过传递指针的方式,将一个变量或函数的内存地址传递出去。直接操作内存是一个高效行为,但过于危险和不可控制,所以在一些语言,如Java、PHP等,不允许直接传递指针,而采用变量引用的方式来实现。
PHP的变量引用,是指不同的变量访问同一变量的内容,语法为&+变量名。例如上例的第2行代码,将$a的引用赋值给$b,即$a和$b指向同一个“地址”,当任何一个变量变化时,都会影响到另一个变量。图3-1中的refcount表示该“地址”被引用的次数。
图3-1 refcount“地址”被引用的次数
3.1.2 引用的取消
设置一个引用变量之后,可以用unset来取消其引用,即销毁引用变量。
程序代码如下:(源码文件:ch03/reference_2.php)
$a = 1; $b = & $a; unset($a); var_dump($b); //output 1
如图3-2所示。
图3-2 变量的引用
可以看到,unset($a)之后,内存中的地址并没有释放,而只是将refcount减1,所以$b的值仍然是1。
3.1.3 forech的引用陷阱
经常有这样的场景:某个数组需要遍历来修改某个值,改完之后需要进行第二次遍历。请看以下程序示例:(源码文件:ch03/reference_foreach.php)
第1个foreach的作用是将数组的每个元素都加1,所以$nums = [2,3]。
第2个foreach的预期是输出2,3,但为什么输出了2,2呢?
请看图3-3所示。
图3-3 foreach的输出
结合上图,我们分析一下为什么出现这种情况。
第2行,定义的数组[1,2],如图3-3所示的第1步。
第3至5行,将数组的每个元素都加1,所以$nums = [2,3],同时数组的第2个元素引入了$num的引用。这造成了后面的意想不到的情况。
第2个foreach处理第1个元素时,$num被赋值为2,同时$num又和第2个元素共享地址,所以第2个元素的值由3变为2。
第2个foreach处理第2个元素时,$num被赋值为2,同时$num又和第2个元素共享地址,相当于把变量本身的值重新赋值给自己,所以第2个元素的值仍然为2。
综上所述,$nums变为了[2,2]。
究其原因,是由于数组最后一个元素的$num引用在foreach循环之后仍会保留。建议使用unset()来将其销毁。
规避的方法有三种:
方法1:不使用变量引用,而用$nums[$index]取出要改变的元素。
(源码文件:ch03/reference_foreach_fix_1.php)
方法2:既然第1个和第2个foreach的变量名相等,那么不妨将第2个foreach的变量更名为$num2,就规避了变量引用的问题,也能输出正确的结果。
程序代码如下:(源码文件:ch03/reference_foreach_fix_2.php)
方法3:unset调引用变量。
程序代码如下:(源码文件:ch03/reference_foreach_fix_3.php)
另外,还有一种使用函数式编程的解决方案,将在第5章讲解。