PHP面试一战到底
上QQ阅读APP看书,第一时间看更新

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章讲解。