![PHP面试一战到底](https://wfqqreader-1252317822.image.myqcloud.com/cover/891/44509891/b_44509891.jpg)
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表示该“地址”被引用的次数。
![](https://epubservercos.yuewen.com/9146EB/23721566801954006/epubprivate/OEBPS/Images/Figure-P55_7497.jpg?sign=1739500752-VHyyMCiqvAnHeLyGKvfRWYBbPPw0Q9ka-0-a42980766afc1256213d7121289bb66f)
图3-1 refcount“地址”被引用的次数
3.1.2 引用的取消
设置一个引用变量之后,可以用unset来取消其引用,即销毁引用变量。
程序代码如下:(源码文件:ch03/reference_2.php)
$a = 1; $b = & $a; unset($a); var_dump($b); //output 1
如图3-2所示。
![](https://epubservercos.yuewen.com/9146EB/23721566801954006/epubprivate/OEBPS/Images/Figure-P55_39631.jpg?sign=1739500752-ToG6z7c5ocq1xGQdixVMyL2W7CN9WiOY-0-5c74056831fb3a478e74e31c8052f222)
图3-2 变量的引用
可以看到,unset($a)之后,内存中的地址并没有释放,而只是将refcount减1,所以$b的值仍然是1。
3.1.3 forech的引用陷阱
经常有这样的场景:某个数组需要遍历来修改某个值,改完之后需要进行第二次遍历。请看以下程序示例:(源码文件:ch03/reference_foreach.php)
![](https://epubservercos.yuewen.com/9146EB/23721566801954006/epubprivate/OEBPS/Images/Figure-P55_39632.jpg?sign=1739500752-pagv5hYKhEbK17o6xz1fL5CAMFijTpbV-0-da098f5461c392ca97e22d82aeadf44f)
第1个foreach的作用是将数组的每个元素都加1,所以$nums = [2,3]。
第2个foreach的预期是输出2,3,但为什么输出了2,2呢?
请看图3-3所示。
![](https://epubservercos.yuewen.com/9146EB/23721566801954006/epubprivate/OEBPS/Images/Figure-P56_7577.jpg?sign=1739500752-bIX1CV9xMBXHhe7k5qS6Pu2VbMFWIPdc-0-5dee63eb3faa2de97094eb2b8aad90c2)
图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)
![](https://epubservercos.yuewen.com/9146EB/23721566801954006/epubprivate/OEBPS/Images/Figure-P57_39638.jpg?sign=1739500752-qDrDD0E9uL5znl8lk7a7DG3KM4mSIxTx-0-6bc8cd46d9c78b9bccfdd3c1672c2171)
方法2:既然第1个和第2个foreach的变量名相等,那么不妨将第2个foreach的变量更名为$num2,就规避了变量引用的问题,也能输出正确的结果。
程序代码如下:(源码文件:ch03/reference_foreach_fix_2.php)
![](https://epubservercos.yuewen.com/9146EB/23721566801954006/epubprivate/OEBPS/Images/Figure-P57_39639.jpg?sign=1739500752-Ycw3RsIub4ZF5yLzjszPYdqK9hxYvbf8-0-7ba69f41a1e7e0ea819eb3b652916031)
方法3:unset调引用变量。
程序代码如下:(源码文件:ch03/reference_foreach_fix_3.php)
![](https://epubservercos.yuewen.com/9146EB/23721566801954006/epubprivate/OEBPS/Images/Figure-P57_39640.jpg?sign=1739500752-eZS1sU7ui31GYoj9jdC1iQXXlRSUw3aO-0-38ff0b04145c5d2efed24a00b3756605)
另外,还有一种使用函数式编程的解决方案,将在第5章讲解。