按引用分配错误

2022-08-30 23:40:53

前几天我遇到了这个看似非常简单的问题 如何在不引用$array 1的情况下更改$array 2中的值?然而,我研究得越多,就越奇怪,这似乎确实按预期运行。在此之后,我开始研究从以下输出生成的操作码。

$array1 = array(2, 10);
$x = &$array1[1];
$array2 = $array1;
$array2[1] = 22;

echo $array1[1]; // Outputs 22

这对我来说似乎很疯狂,因为array2应该只是array1的副本,而发生在一个数组上的任何事情都不应该影响另一个数组的内容。当然,如果您注释掉第二行,最后一行将像预期的那样回显出10行。

再往前看,我可以找到一个很酷的网站,向我展示PHP使用Vulcan Logic Dumper生成的操作码。这是上述代码生成的操作码。

Finding entry points
Branch analysis from position: 0
Return found
filename:       /in/qO86H
function name:  (null)
number of ops:  11
compiled vars:  !0 = $array1, !1 = $x, !2 = $array2
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   3     0  >   INIT_ARRAY                                       ~0      2
         1      ADD_ARRAY_ELEMENT                                ~0      10
         2      ASSIGN                                                   !0, ~0
   4     3      FETCH_DIM_W                                      $2      !0, 1
         4      ASSIGN_REF                                               !1, $2
   5     5      ASSIGN                                                   !2, !0
   6     6      ASSIGN_DIM                                               !2, 1
         7      OP_DATA                                                  22, $6
   8     8      FETCH_DIM_R                                      $7      !0, 1
         9      ECHO                                                     $7
        10    > RETURN                                                   1

这些操作码在这里没有很好的记录 http://php.net/manual/en/internals2.opcodes.php 但我相信在英语中,操作码正在执行以下操作。按行...对我来说可能比任何人都多。

  1. 第 3 行:我们使用数组的第一个值初始化数组,然后向其添加 10,然后再将其分配给 $array 1。
  2. 第 4 行:获取只写?值,并通过引用$x来分配它。
  3. 第 5 行:将 $array 1 设置为 $array 2。
  4. 第 6 行:获取数组索引 1。od_data我猜把它设置为22,尽管6美元永远不会返回。OD_DATA绝对没有文档,也没有被列为我看过的任何地方的操作码。
  5. 第 8 行:从 $array 1 的索引 1 中获取只读值并将其回显出来。

即使通过操作码工作,我也不确定哪里出了问题。我有一种感觉,缺乏关于操作码的文档,以及我对使用它们的经验不足,可能会让我无法弄清楚哪里出了问题。

编辑1:

正如 Mike 在第一个注释数组中指出的那样,引用状态在复制时会保留。这里可以看到文档以及它链接到 http://php.net/manual/en/language.types.array.php#104064 的数组文章中的一个位置。这足够有趣并不被认为是警告。如果这是真的,我感到惊讶的是,此代码的引用状态不会像您预期的那样保留。

$array1 = array(2, 10);
$x = &$array1;
$array2 = $array1;
$array2[1] = 22;

echo $array1[1]; // Output is 10

因此,似乎只有在您尝试通过引用分配单个元素时才会发生这种情况,从而使此功能更加混乱。

为什么 php 只在单独分配数组索引时保留它们的状态?

编辑2:

我今天使用HHVM进行了一些测试,HHVM以您认为的方式处理代码的第一个片段。我喜欢PHP,但HHVM比Zend引擎看起来越来越好。


答案 1

这在PHP手册中进行了解释(即使您必须花费比您应该花费更多的时间才能找到它),特别是在 http://php.net/manual/en/language.types.array.php#104064

“共享”数据保持共享,初始分配仅充当别名。直到你开始用独立的操作来操作数组,就像这样,intepreter才开始将它们视为发散列表,即使这样,共享数据仍然保持共享,这样你就可以有两个数组,其中共享的前n个元素,但发散的后续数据。...[] = ...

对于一个数组到另一个数组的真正“按值复制”,您几乎最终会执行类似的事情

$arr2 = array();
foreach($arr1 as $val) {
  $arr2[] = $val;
}

$arr2 = array();
for($i=count($arr1)-1; $i>-1; $i--) {
  $arr2[$i] = $arr[$i];
}

(使用反向循环主要是因为没有足够的人记住这是你可以做的事情,并且比正向循环更有效=)

你会想象会有一个函数或其他东西来帮助处理数组复制怪癖,但似乎没有。这很奇怪,但是那些“PHP的状态”之一。过去曾有过这样的选择,PHP因此与这个选择一起生活了好几年,所以它只是“其中之一”。不幸!array_copy


答案 2

推荐