我有这个php代码
$a = array('one');
$b[0] = &$a[0];
$c = $a;
$b[0] = 'three';
$a = array('two');
var_dump($a); echo '<br/>';
var_dump($b); echo '<br/>';
var_dump($c); echo '<br/>';
输出此 ->
array(1) { [0]=> string(3) "two" }
array(1) { [0]=> &string(5) "three" }
array(1) { [0]=> &string(5) "three" }
$b 和 $a 指向相同的值,但$c不应指向相同的值,它应该有自己的值副本。然后,当我将$b的值更改为 3 时,我不明白为什么$c的值也会发生变化。我该如何防止这种情况?另外,当我将$a的值更改为"二"时,为什么$b不也更改为"二"?
提前谢谢。
你的问题是:
$b[0] = &$a[0];
这引用了数组的第一个元素。从var_dump()
输出&string(5)
可以看出。
所以$b
不引用整个数组,只引用第一个元素。
要引用整个数组,您需要执行以下操作:
$b = &$a;
这样做可以按预期工作:
// $a
array(1) {
[0]=>
string(3) "two"
}
// $b
array(1) {
[0]=>
string(3) "two"
}
// $c
array(1) {
[0]=>
string(3) "one"
}
阅读更多关于 PHP 中的数组。
这是一个分步说明:
$a = array('one');
$b[0] = &$a[0];
$b
现在是一个数组,它的第一个元素引用$a
的第一个元素。在符号表中,$b[0]
和 $a[0]
指向相同的基础 zval。
$c = $a;
$c
引用现在$a
,但尚未创建副本(写入语义上的复制(。它看起来很像这样:
$c ----/----> $a $b
(0: 'one') 0: 'one' <---- 0: 'one'
下一条语句:
$b[0] = 'three';
这也更新$a[0]
,进而更新$c[0]
,因为$a
本身不会改变。它现在看起来像;
$c ----/----> $a $b
(0: 'three') 0: 'three' <---- 0: 'three'
下一条声明:
$a = array('two');
断开$a
与$c
的连接,断开$a[0]
与$b[0]
的连接。
$c $a $b
0: 'three' 0: 'two' 0: 'three'
预防
为了防止这种行为,你需要引用整个数组,而不仅仅是一个元素:
$b = &$a;
通过这样做,$b
和$a
现在引用相同的 zval(数组本身(,因此所做的任何更改都会将其与 $c
断开连接。
不过,我会考虑该语言的这些边缘情况,如果您不需要,我建议您不要使用引用。
了解按引用与值分配时正在执行的操作非常重要。让我们一行一行地进行。
$a = array('one');
$a指向 M1,在 M1 中我们有一个包含 1 个条目的数组,我们称之为 M1-A1。
$b[0] = &$a[0];
现在我们要做的是将$b[0]指向M1-A1。请记住,$a[0] 也指向 M1-A1,因此两者都指向内存的这一特定部分。请记住,$b本身有自己的内存位置 M2,但在 M2 中我们指向 M1-A1(即 M2-A1 指向 M1-A1(。
$c = $a;
由于我们不是通过引用分配的,我们得到的是一个新的内存位置,我们称之为 M3,但在 M3 中有一个数组,第一个条目仍然指向 M1-A1。
所以现在我们有 M1、M2、M3,在 M2 和 M3 中有一个指向 M1-A1 的数组条目。
$b[0] = 'three';
由于$b[0]实际上指向M1-A1,我们实际上正在更改M1-A1记忆点的值。因此,任何引用M1-A1位置的东西也会看到这个值的变化。
$a = array('two');
此时,我们正在完全更改$a的内存位置。最初是 M1,现在我们正在创建一个新的内存位置 M4。没有其他东西指向 M4,M4-A1 也不指向 M1-A1。
因此,当我们进行 var 转储时,我们会得到您提到的值。
我可能使这更加混乱,但尝试将其画在纸上,它会很清楚。了解所有内容都存储在内存中,变量仅指向内存位置。一旦你理解了这个原则,一切都会到位。
它们都引用内存中的同一数组对象