引用不是什么:引用。
引用是不透明的东西,就像指针一样,除了 A) 更智能和 B) 指向 HLL 对象,而不是内存地址。PHP 没有引用。PHP 有一种语法用于创建*别名*,它们是同一个对象的多个名称。PHP 有几个语法片段用于按“引用”调用和返回,这实际上只是意味着抑制复制。在本手册的“引用”部分中,没有任何引用。
如前所述,引用不是指针。这意味着,以下结构不会按预期执行
<?php
function foo(&$var)
{
$var =& $GLOBALS["baz"];
}
foo($bar);
?>
发生的情况是 $var 在 foo 中将与调用者中的 $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); // 出现错误:*** 对象 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 引用所做的