說明
這裡基於php7.2.5進行測試,php7之後內部結構變化應該不是太大,但與php5.X有差別。
什麼是引用
在PHP中引用是一種資料型別(結構),是指 指向同一個型別的資料結構,來看具體儲存結構
struct _zend_reference {
// 引用計數用於垃圾回收 先忽略
zend_refcounted_h gc;
// zval是另一個變數 zval還記得嗎 儲存變數的結構
// 這裡val指向另一個zval
zval val;
};
如何使用
// 定義變數
$a = "hello";
// &生成引用變數
$b = &$a;
echo $b;
echo PHP_EOL;
php hello.php
hello
結果大家都知道,$b與$a的值是相同的,而且你還知道,修改$b同時也會作用與$a.
$a = "hello";
$b = &$a;
echo "b:".$b;
echo PHP_EOL;
$b = "world";
echo "a:".$a;
echo PHP_EOL;
php hello.php
b:hello
a:world
如何實現
回憶zval的格式執行第一句時,生成的資料結構是這樣的(簡版示意圖,真實結構複雜)
當執行$b = &$a時,&$a會先生成引用型別的資料結構,然後引用的zval指向之前的hello的結構,$a,$b則共同指向引用結構。
那麼在修改$b時,實際是修改了引用型別指向的zend_value,所以導致$a的值也發生了變化。
小結
php中使用&生成一個引用型別的資料,這個引用的zval指向原變數所指向的zval, 變數則會指向這個應用結構,當發生引用賦值之後,被賦值的變數也會指向這個引用,更改其中任何一個變數,所有的變數都會發生變化。類似於你大名叫大壯, 小名叫小壯, 但是身份證都是xxoo100, 無論修改大壯還是小壯的身份證,他始終只有一個身份證。
擴充套件
我們可能聽過取地址符這麼一種說法,那麼在C語言或者GO中,通過使用&可以獲取到變數的記憶體地址。
#include <stdio.h>
#include<string.h>
int main()
{
char str[] = "hello";
// &獲取變數的記憶體地址
printf("%p\n", &str);
}
gcc pointer.c -o pointer
./pointer
0x7ffee6d3292a
但是PHP的&並不是獲取變數的地址,這是需要注意的。
unset引用變數
unset($b);
echo "a:".$a;
echo PHP_EOL;
echo "b:".$b;
echo PHP_EOL;
php hello.php
a:world
PHP Notice: Undefined variable: b
unset($b)之後,只是銷燬了變數b及b對引用的指向,沒有影響$a。
foreach中的引用
$list = [
['id' => 3, 'total' => 3],
['id' => 4, 'total' => 4],
['id' => 5, 'total' => 5],
];
foreach ($list as $key => &$info) {
$info['total'] = $info['total'] + 3;
}
print_r($list);
// 一頓操作
$info['name'] = "hello";
print_r($info);
php hello.php
Array
(
[0] => Array
(
[id] => 3
[total] => 6
)
[1] => Array
(
[id] => 4
[total] => 7
)
[2] => Array
(
[id] => 5
[total] => 8
)
)
Array
(
[id] => 5
[total] => 8
[name] => hello
)
這裡在foreach之後需要把info unset掉防止發生資料問題。
總結
PHP中通過&獲取對變數的引用,實質是多個變數通過中間引用型別(不是指向記憶體地址),指向同一個值。
本作品採用《CC 協議》,轉載必須註明作者和本文連結