什麼不是參考:參考。
參考是不透明的東西,它們就像指標,除了 A) 更聰明,以及 B) 參考 HLL 物件,而不是記憶體位址。PHP 沒有參考。PHP 有一種語法,用於建立 *別名*,它們是同一個物件的多個名稱。PHP 有一些語法可以用「參考」方式呼叫和回傳,這實際上只是表示抑制複製。在本手冊的「參考」章節中,沒有任何地方出現參考。
如前所述,參考不是指標。這表示,以下結構不會如您預期般運作
<?php
function foo(&$var)
{
$var =& $GLOBALS["baz"];
}
foo($bar);
?>
發生的是,foo 中的 $var 會與呼叫者中的 $bar 繫結,然後與 $GLOBALS["baz"] 重新繫結。無法使用參考機制將呼叫範圍中的 $bar 繫結到其他內容,因為 $bar 在函式 foo 中不可用(它由 $var 表示,但 $var 僅具有變數內容,而沒有呼叫 符號表 中的名稱到值的繫結)。您可以使用回傳參考來參考函式選取的變數。
什麼不是參考:參考。
參考是不透明的東西,它們就像指標,除了 A) 更聰明,以及 B) 參考 HLL 物件,而不是記憶體位址。PHP 沒有參考。PHP 有一種語法,用於建立 *別名*,它們是同一個物件的多個名稱。PHP 有一些語法可以用「參考」方式呼叫和回傳,這實際上只是表示抑制複製。在本手冊的「參考」章節中,沒有任何地方出現參考。
文字中給出的範例
<?php
function foo(&$var)
{
$var =& $GLOBALS["baz"];
}
foo($bar);
?>
說明(至少在我看來)為什麼 = 和 & 應該寫在一起作為一種新的替代運算子,而不是像 C 中那樣分開寫,像 $var = &$GLOBALS["baz"];
使用全新的術語
對我而言,這個函式的結果並不令人驚訝,因為 =& 表示「將 $var 的「目標」從它所在的位置變更為與 $GLOBALS["baz"] 的目標相同」。好吧,它「是」實際的參數 $bar,但現在它將是「baz」的全域變數。
如果您只是移除替代中的 &,它會將 $GLOBALS["baz"] 的值放入 $var 的目標,也就是 $bar(除非 $bar 已經是參考,否則值會進入該目標)。
總而言之,= 會取代「目標」的值;=& 會變更目標。
您可以將一個陣列的值參考到另一個陣列的值,
但是,如果您透過重新指定陣列來變更陣列,則參考將不再適用,例如
<?php
$ref = [1,2,3];
$c = count($ref);
$foo = ['A'];
for($i=0;$i<$c;$i++)
$foo[] =& $ref[$i];
print_r($foo);
print_r($ref);
$ref = [4,5,6];
print_r($foo);
print_r($ref);
?>
將輸出
陣列
(
[0] => A
[1] => 1
[2] => 2
[3] => 3
)
陣列
(
[0] => 1
[1] => 2
[2] => 3
)
陣列
(
[0] => A
[1] => 1
[2] => 2
[3] => 3
)
陣列
(
[0] => 4
[1] => 5
[2] => 6
)
因此,如果您希望值仍然參考,則必須個別設定陣列值,而不要重新指定陣列
<?php
$ref = [1,2,3];
$c = count($ref);
$foo = ['A'];
for($i=0;$i<$c;$i++)
$foo[] =& $ref[$i];
print_r($foo);
print_r($ref);
$bar = [4,5,6];
foreach($bar as $i => $value)
$ref[$i] = $value;
print_r($foo);
print_r($ref);
?>
結果
陣列
(
[0] => A
[1] => 1
[2] => 2
[3] => 3
)
陣列
(
[0] => 1
[1] => 2
[2] => 3
)
陣列
(
[0] => A
[1] => 4
[2] => 5
[3] => 6
)
陣列
(
[0] => 4
[1] => 5
[2] => 6
)
我有點同意上面的使用者所說,PHP 參考實際上比 JAVA 或 C++ 更像 C 指標。
原因如下。
它們的作用基本上與指標完全相同。
unset($x) 和 free(x) 之間的差異似乎是 unset() 和 free() 運算子之間的差異,而不是參考和指標本身之間的差異。
Free 會解除配置堆疊上的記憶體。
Unset 只是移除變數。
除此之外,我們在這裡處理的是兩個非常簡單的結構。
我認為在指定物件時,術語會讓人感到困惑。
嘗試像這樣思考繫結和參考
<?php
# 程式碼:
$a = 5; $b =& $a; $c = new stdClass(); $d = $c;
# 背後的符號表和值:
$global_names = array(
'a' => array('binding' => 0),
'b' => array('binding' => 0),
'c' => array('binding' => 1),
'd' => array('binding' => 2),
);
$values = array(
0 => array('type' => 'scalar', 'value' => 5),
1 => array('type' => 'objId', 'value' => 0),
2 => array('type' => 'objId', 'value' => 0)
);
?>
$a 綁定(或參考,或是對...的參考)到索引 0(純量 5)的值。
$b 綁定到與 $a 相同的東西--索引 0(純量 5)的值。
$c 綁定到索引 1(物件 ID 0)的值。
$d 綁定到索引 2 的值(一個獨立且不同的值,也指向物件 ID 0)。
當文件指出您不能從範例函式 foo 內[重新]綁定 $bar 到其他東西時,這表示您不能更改在我這個虛擬引擎中會是 $global_names['bar']['binding'] 的內容。您只能更改 $values[$names['var']['binding']] (使用 "$var ="; 與 $values[$global_names['bar']['binding']] 參考/綁定的相同值) 或 $names['var']['binding'] (使用 "$var =&")。
也請考慮這段程式碼
<?php
$a = 3; $b =& $a;
function foo (&$c) { $c = new stdClass(); }
function bar () { return new stdClass(); }
function &fum () { return new stdClass(); }
if (!is_object($a)) { echo "\$a 最初不參考物件\n"; }
foo($b);
echo "\$b ", ($a === $b)? "尚未": "已經", "被 foo 重新綁定\n";
if (is_object($a)) { echo "\$a 現在包含一個物件識別符\n"; }
$b =& bar();
echo "\$b ", ($a === $b)? "尚未": "已經", "被 bar 重新綁定\n";
$b =& fum();
echo "\$b ", ($a === $b)? "尚未": "已經", "被 fum 重新綁定\n";
?>
輸出結果為
$a 最初不參考物件
$b 尚未被 foo 重新綁定
$a 現在包含一個物件識別符
$b 尚未被 bar 重新綁定
$b 已經被 fum 重新綁定
換句話說,值可以被更改,但綁定不會(除非回傳參考),正如所述。
物件識別符確實使物件「值」像指標一樣工作(但沒有達到 C/C++ 的程度,而且不像參考)。
「參考不像指標」的說法有點令人困惑。
在範例中,作者展示了如何將參考指派給也是參考的形參,不會影響實參的值。這與 C 中指標的行為完全相同。唯一的區別是,在 PHP 中,您不必對指標進行解參考來取得該值。
-+-+-
int bar = 99;
void foo(int* a)
{
a = &bar;
}
int main()
{
int baz = 1;
foo(&baz);
printf("%d\n", baz);
return 0;
}
-+-+-
輸出將是 1,因為 foo 沒有將值指派給解參考後的形參。相反,它在 foo 的範圍內重新指派了形參。
或者,
-+-+-
int bar = 99;
void foo(int* a)
{
*a = bar;
}
int main()
{
int baz = 1;
foo(&baz);
printf("%d\n", baz);
return 0;
}
-+-+-
輸出將是 9,因為 foo 在指派之前解參考了形參。
因此,雖然語法上存在差異,但 PHP 參考實際上非常像 C 中的指標。
我同意 PHP 參考與 Java 參考非常不同,因為 Java 沒有任何機制可以將值指派給參考,以這種方式修改實參的值。
可以做一些類似於指標(如 C 中)的事情,其中類似 array(&$a) 是指向一個名為 $a 的變數的指標;這個值可以作為一個值傳遞;它不是一個變數或別名之類的,而是一個實際的值。
然後您可以使用如下程式碼,通過指標進行讀/寫
<?php
function get($x) { return $x[0]; }
function put($x,$y) { $x[0]=$y; }
?>
如上所述,參考不是指標。
以下範例顯示指標和參考之間的差異。
這段程式碼
<?
$b = 1;
$a =& $b;
print("<pre>");
print("\$a === \$b: ".(($a === $b) ? "ok" : "failed")."\n");
print("取消設定 \$a...\n");
unset($a);
print("現在 \$a 是 ".(isset($a) ? "已設定" : "未設定")." 而 \$b 是 ".(isset($b) ? "已設定" : "未設定")."\n");
print("</pre>");
$b = 1;
$a =& $b;
print("<pre>");
print("\$a === \$b: ".(($a === $b) ? "ok" : "failed")."\n");
print("取消設定 \$b...\n");
unset($b);
print("現在 \$a 是 ".(isset($a) ? "已設定" : "未設定")." 而 \$b 是 ".(isset($b) ? "已設定" : "未設定")."\n");
print("</pre>");
?>
將產生以下輸出
---------
$a === $b: ok
取消設定 $a...
現在 $a 是未設定的,而 $b 是已設定的
$a === $b: ok
取消設定 $b...
現在 $a 是已設定的,而 $b 是未設定的
---------
因此您可以看到 $a 和 $b 是相同的 ($a === $b -> true),但如果其中一個被取消設定,另一個不會受到影響。
考慮這段程式碼
<?php
$arr = ['1', '2', '3', '4'];
foreach ($arr as &$i) {}
echo implode($arr, ', ')."\n";
foreach ($arr as $i) {}
echo implode($arr, ', ')."\n";
?>
將輸出
1, 2, 3, 4
1, 2, 3, 3
儘管似乎沒有對陣列進行任何變更。
陣列中的最後一個項目被覆寫,因為在第二次迭代中,參考被副本取代。更詳細地說,它首先被 1 覆寫,然後被 2 覆寫,然後被 3 覆寫,然後再次被 3 覆寫。
請確保在使用相同變數名稱以參考迭代之後,使用副本執行迭代時,執行 unset($i)!
我認為以下程式碼可以說明 PHP 參考和 C 指標之間的差異
在 PHP 中
<?php
$a = 0;
$b = &a;
echo $a; //0
unset($b); // 取消設定 $b
echo $a; //0 沒問題
?>
在 C 中
#include <stdio.h>
int main(int argc, char const *argv[]) {
int a = 0;
int* b = &a;
printf("%i\n", a); //0
free(b); // 釋放 b
printf("%i\n", a); //取得錯誤:*** object 0x7fff6350da08 的錯誤:正在釋放的指標未被分配
}
我理解成這樣
PHP 中的參考就像在 C/C++ 中為自己的變數建立單一指標,並指向變數(沒有指標算術,而且我們無法取得記憶體中變數的位址數字)。
例如
<?php
$a = 4;
$b = &$a;
$c = &$b;
echo "$a - $b - $c<br>";
// 3 個指標 (a, b, c) 指向記憶體中儲存數值為 4 的位置。
$c = 5;
echo "$a - $b - $c<br>";
// 所有變數都等於 5;
unset($a);
$c = 6;
echo "$a - $b - $c<br>";
//$a 不存在,但它只是一個指標 (不是記憶體的實際部分),所以我們無法取得或變更它的值。
?>
----
當我們想在 PHP 中建立類似「指標的指標」時,是無法做到的,因為這在 PHP 中是不可能的。我們需要一個指向另一個指標的指標,才能改變該指標所指向的位置。在你的範例中,你只是在函式中改變變數的值。(沒有指標操作)
一個不是那麼簡單的變通方法...但仍然可行...玩得開心
class My{
var $value;
function get1(&$ref){
$ref[] =& $this;
}
function get2(&$ref){
$ref =& $this;
}
function get3(&$ref){
$ref = $this;
}
}
$m = new My();
$m->value = 'foo';
$m->get1($ref=array());
$m1 =& $ref[0];
$m1->value = 'bar';
echo "\n".'可行但很醜...';
echo "\n".' m:'. get_class($m) . '->value = '. $m->value;
echo "\n".' m1:'. get_class($m1) . '->value = '. $m1->value;
echo "\n".'因為參考不是指標,所以無法運作...';
$m->value = 'foo';
$m->get2($m2);
$m2->value = 'bar';
echo "\n".' m:'. get_class($m) . '->value = '. $m->value;
echo "\n".' m2:'. get_class($m2) . '->value = '. $m2->value;
$m->value = 'foo';
$m->get3($m3);
$m3->value = 'bar';
echo "\n".'因為設定為副本,所以無法運作';
echo "\n".' m:'. get_class($m) . '->value = '.$m->value;
echo "\n".' m3:'. get_class($m3) . '->value = '. $m3->value;
<?php
function foo(&$var)
{
$var =& $GLOBALS["baz"];
}
foo($bar);
?>
讓我們用另一種方式重寫上面的程式碼片段
<?php
$bar = &$var; // 在此時,$bar 參考 $var 所參考的內容,也就是 NULL 或空值。
$var = & $GLOBALS["baz"]; // 在此時,$var 變更為參考全域變數 $baz 所參考的另一個內容。然而,$bar 仍然參考 NULL 的內容。
預期結果: $bar 將會持有 $var 的內容(現在是 $baz 的內容)
實際結果: $bar 持有 NULL 或空值。這就是 PHP 參考的運作方式