【问题标题】:PHP Reference in array key数组键中的 PHP 参考
【发布时间】:2014-11-23 19:00:16
【问题描述】:

PHP:

$a = array("key" => 23);
var_dump($a);

$c = &$a["key"];
var_dump($a);

unset($c);
var_dump($a);

输出:

array(1) {
  ["key"]=>
  int(23)
}
array(1) {
  ["key"]=>
  &int(23)
}
array(1) {
  ["key"]=>
  int(23)
}

在第二个转储中,“key”的值显示为参考。这是为什么? 如果我对普通变量而不是数组键执行相同操作,则不会发生这种情况。

我唯一的解释是数组键通常存储为引用,只要符号表中只有一个条目,它就会在转储中显示为标量。

【问题讨论】:

  • 是的,似乎 PHP 识别出您使用了引用,因此也将数组中的值替换为相同的引用。因此,如果您更新两个值之一,另一个值也会更新。
  • 虽然如果我使用普通变量而不是数组键则不会发生这种情况。
  • stackoverflow.com/q/17528280/476 的副本;但是,下面的答案太好了,我不想关闭它。

标签: php arrays reference key


【解决方案1】:

在内部,PHP 数组是 hashmaps(或字典,或 HashTables 或任何你想称呼它的东西)。甚至一个数字索引数组也被实现为一个哈希表,它是一个zval,就像任何其他的一样。
但是,您看到的是预期行为,both herehere 对此进行了解释。

基本上,您的数组在内部看起来是这样的:

typedef struct _zval_struct {
    zvalue_value value;
    zend_uint refcount__gc;
    zend_uchar type;
    zend_uchar is_ref__gc;
} zval;
//zval_value:
typedef union _zvalue_value {
    long lval;
    double dval;
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;
    zend_object_value obj;
} zvalue_value;

如果是数组,zval.type 将被设置为表示zval 值是一个数组,因此将使用zval_value.ht 成员。
当您编写$c = &$a['key'] 时会发生什么,分配给$a['key']zval 将被更新:zval.refcount__gc 将递增,is_ref__gc 将设置为 1。因为 value 未被复制,但该值被多个变量使用:这意味着该值 一个引用。一旦你unset($c);refcount 就会减少,并且引用会丢失,所以is_ref 被设置为0

现在是大问题:当您使用常规的标量变量时,为什么看不到相同的东西?嗯,这是因为数组是一个 HashTable,具有自己的内部引用计数 (zval_ptr_dtor)。一旦数组本身为空,它也应该被销毁。通过创建对数组值的引用并取消设置数组,zval 应该被 GC'ed。但这意味着你有一个被破坏的zval 的引用。
因此,数组中的zval 也被更改为引用:可以安全地删除引用。因此,如果您要这样做:

$foo = array(123);
$bar = &$foo[0];
unset($foo[0]);
echo $bar, PHP_EOL;

您的代码仍将按预期工作:$foo[0] 不再存在,但 $bar 现在是对 123 的唯一现有引用。

这只是一个非常非常简短且不完整的解释,但是请在 Google 上搜索 PHP 内部结构、内存管理的工作原理、内部如何处理引用以及垃圾收集器如何使用 is_refrefcount成员来管理内存。
特别注意写时复制等内部机制,并且(查看我在此处提供的第一个链接时)寻找如下所示的 sn-p:

$ref = &$array;
foreach ($ref as $val) {}

因为它在引用和数组方面处理了一些奇怪的问题。

【讨论】:

  • 写得很详细,谢谢。在阅读了 PHP 的内部结构后,我仍然很困惑为什么普通变量不会发生这种情况,而只有数组中的键会发生这种情况。根据 zval 结构,每个内容被多次引用的变量都应显示为 var_dump 上的引用。仍然只有当我引用数组中的键时才会发生。
  • @Megatron:这与为 HashTable(数组)进行的引用计数有关。因为您引用的值应该保持不变,即使在对数组进行 GC 处理后,数组也会保存对 zval 的引用,而不是对 zval 本身的引用。实际的 zval 被搁置一旁,只有在没有变量引用它的情况下才会被 GC(严重过度简化,但你明白了)
  • 啊,聪明,我没想到。现在一切对我来说似乎都清楚了。谢谢你:)
  • @Megatron:不客气。 [pedantic]请注意,在帮助部分,您被要求不要发布“谢谢” cmets,而是为您认为有帮助的答案投票(接受和/或投票)
猜你喜欢
  • 1970-01-01
  • 2015-06-09
  • 1970-01-01
  • 2019-11-29
  • 1970-01-01
  • 2015-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多