PHP 日本研討會 2024

什麼不是參考

如前所述,參考不是指標。這表示,以下結構不會如您預期般運作

<?php

function foo(&$var)
{
$var =& $GLOBALS["baz"];
}

foo($bar);

?>

發生的是,foo 中的 $var 會與呼叫者中的 $bar 繫結,然後與 $GLOBALS["baz"] 重新繫結。無法使用參考機制將呼叫範圍中的 $bar 繫結到其他內容,因為 $bar 在函式 foo 中不可用(它由 $var 表示,但 $var 僅具有變數內容,而沒有呼叫 符號表 中的名稱到值的繫結)。您可以使用回傳參考來參考函式選取的變數。

新增註解

使用者貢獻的註解 13 則註解

174
Andrew
16 年前
什麼不是參考:參考。

參考是不透明的東西,它們就像指標,除了 A) 更聰明,以及 B) 參考 HLL 物件,而不是記憶體位址。PHP 沒有參考。PHP 有一種語法,用於建立 *別名*,它們是同一個物件的多個名稱。PHP 有一些語法可以用「參考」方式呼叫和回傳,這實際上只是表示抑制複製。在本手冊的「參考」章節中,沒有任何地方出現參考。
81
匿名
16 年前
文字中給出的範例

<?php
function foo(&$var)
{
$var =& $GLOBALS["baz"];
}
foo($bar);
?>

說明(至少在我看來)為什麼 = 和 & 應該寫在一起作為一種新的替代運算子,而不是像 C 中那樣分開寫,像 $var = &$GLOBALS["baz"];

使用全新的術語

對我而言,這個函式的結果並不令人驚訝,因為 =& 表示「將 $var 的「目標」從它所在的位置變更為與 $GLOBALS["baz"] 的目標相同」。好吧,它「是」實際的參數 $bar,但現在它將是「baz」的全域變數。

如果您只是移除替代中的 &,它會將 $GLOBALS["baz"] 的值放入 $var 的目標,也就是 $bar(除非 $bar 已經是參考,否則值會進入該目標)。

總而言之,= 會取代「目標」的值;=& 會變更目標。
3
bravo1romeo
7 年前
您可以將一個陣列的值參考到另一個陣列的值,
但是,如果您透過重新指定陣列來變更陣列,則參考將不再適用,例如

<?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
)
3
leonardp122794 at gmail dot com
7 年前
我有點同意上面的使用者所說,PHP 參考實際上比 JAVA 或 C++ 更像 C 指標。

原因如下。

它們的作用基本上與指標完全相同。

unset($x) 和 free(x) 之間的差異似乎是 unset() 和 free() 運算子之間的差異,而不是參考和指標本身之間的差異。

Free 會解除配置堆疊上的記憶體。

Unset 只是移除變數。

除此之外,我們在這裡處理的是兩個非常簡單的結構。
6
briank at kappacs dot com
13 年前
我認為在指定物件時,術語會讓人感到困惑。

嘗試像這樣思考繫結和參考

<?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++ 的程度,而且不像參考)。
9
ansonyumo at email dot com
20 年前
「參考不像指標」的說法有點令人困惑。

在範例中,作者展示了如何將參考指派給也是參考的形參,不會影響實參的值。這與 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 沒有任何機制可以將值指派給參考,以這種方式修改實參的值。
1
Anonymous
8 年前
可以做一些類似於指標(如 C 中)的事情,其中類似 array(&$a) 是指向一個名為 $a 的變數的指標;這個值可以作為一個值傳遞;它不是一個變數或別名之類的,而是一個實際的值。

然後您可以使用如下程式碼,通過指標進行讀/寫
<?php
function get($x) { return $x[0]; }
function
put($x,$y) { $x[0]=$y; }
?>
5
christian at kno dot at
23 年前
如上所述,參考不是指標。

以下範例顯示指標和參考之間的差異。

這段程式碼
<?
$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),但如果其中一個被取消設定,另一個不會受到影響。
1
ArticIce(Juice)
9 年前
考慮這段程式碼

<?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)!
8
shuimuqingshu at gmail dot com
12 年前
我認為以下程式碼可以說明 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 的錯誤:正在釋放的指標未被分配
}
2
Anonymous
14 年前
我理解成這樣
 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 中是不可能的。我們需要一個指向另一個指標的指標,才能改變該指標所指向的位置。在你的範例中,你只是在函式中改變變數的值。(沒有指標操作)
-1
schultz __at__ widescreen __dot__ ch
20 年前
一個不是那麼簡單的變通方法...但仍然可行...玩得開心

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;
-1
minhtrung2606 at gmail dot com
5 年前
<?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 參考的運作方式
To Top