PHP 的基本赋值运算(传值赋值)是将变量的值拷贝到新变量中(=),事实上生成了一个独立的变量,改变其中一个不会影响另一个。也有另一种引用赋值(=&),事实上是给变量增加了一个引用,两个变量事实上存储了同一个内容,改变其中任何一个都是等效的。
在传递函数参数的时候,也有和赋值相似的情况:如果将一个变量传递给一个函数,那么这个函数只能修改这个变量的一个拷贝;如果将一个变量的引用传递给一个函数,那么这个函数则可以修改这个变量的内容。
这就带来了基本赋值没有的问题,无论是在函数内外,还是 unset 引用的时候,都会比基本赋值要复杂一点。与其解释引用在这些情况下的行为,不如先了解引用的工作方式。好在,php.net 上有个用户评论,很好地回答了这个问题,我把它翻译如下。读了它以后,你就能对引用有一个直观的理解。
首先,想象这样一个内存:
地址 | 值 | 引用 | |
---|---|---|---|
1 | NULL | — | |
2 | NULL | — | |
3 | NULL | — | |
4 | NULL | — | |
5 | NULL | — |
创建几个变量:
$a = 10;
$b = 20;
$c = array(1, 2, 3);
内存变成了这样:
地址 | 值 | 引用 | |
---|---|---|---|
1 | 10 | $a | |
2 | 20 | $b | |
3 | 1 | $c[0] | |
4 | 2 | $c1 | |
5 | 3 | $c[2] |
来个引用赋值:
$a =& $c[2];
地址 | 值 | 引用 | |
---|---|---|---|
1 | NULL | — | 这块唯一的引用 $a 指向别处了,所以块内容被垃圾回收销毁 |
2 | 20 | $b | |
3 | 1 | $c[0] | |
4 | 2 | $c1 | |
5 | 3 | $c[2], $a | 现在 $a 成了这块的引用 |
再来个引用赋值:
$b =& $a; // 或者 $b =& $c[2]; 也是同样的效果
地址 | 值 | 引用 | |
---|---|---|---|
1 | NULL | — | |
2 | NULL | — | 这块也没有引用了,被销毁 |
3 | 1 | $c[0] | |
4 | 2 | $c1 | |
5 | 3 | $c[2], $a, $b | 现在 $b 也成了这块的引用 |
现在 unset 掉一个:
unset( $c[2] );
地址 | 值 | 引用 | |
---|---|---|---|
1 | NULL | — | |
2 | NULL | — | |
3 | 1 | $c[0] | |
4 | 2 | $c1 | |
5 | 3 | $a, $b | 虽然 $c[2] 没了,但这块还有引用,所以不会被销毁 |
再来个基本赋值:
$c[2] = 500;
地址 | 值 | 引用 | |
---|---|---|---|
1 | 500 | $c[2] | 新插入了一个数据,放进内存中的空位 |
2 | NULL | — | |
3 | 1 | $c[0] | |
4 | 2 | $c1 | |
5 | 3 | $a, $b | 这块被占用了,所以新数据不会放在这儿 |
如果想让 $c[2] 引用它最早先引用的那块数据(地址 5):
$c[2] =& $a; // 或者 $c[2] =& $b; 也是同样的效果
unset($a);
unset($b);
地址 | 值 | 引用 | |
---|---|---|---|
1 | NULL | — | |
2 | NULL | — | |
3 | 1 | $c[0] | |
4 | 2 | $c1 | |
5 | 3 | $c[2] | $c[2] 回来了,$a $b 被销毁 |
读完这个用户评论,引用的工作方式就清楚了。所以,最好的理解引用的方式,就是“引用是一个变量的别名”。事实上,新建变量时的变量名和后来为这个变量增加的引用,都可以看作是“引用”,事实上它们都是对同一块内存数据的引用,它们之间没有什么本质的区别。
不过值得注意的是,这个“别名”也遵循一般变量的生存周期。也就是说,在函数内给一个函数外的变量(在函数内声明为 global)生成一个引用,这个引用的生命周期同样仅限于函数内。就像是你上中学的时候,同学们给你取了个外号,但是当你升入大学以后,没有人会知道你有那个外号。
$str1 = 'I am serious.';
$str2 = 'Why so serious?';
function some_func() {
global $str1, $str2;
$str2 =& $str1;
}
some_func();
echo $str2; // Why so serious?
看起来,虽然大家都是引用,也得看出身啊:)。