数组键中的PHP引用

数组键中的PHP引用,php,arrays,reference,key,Php,Arrays,Reference,Key,PHP: 输出: $a = array("key" => 23); var_dump($a); $c = &$a["key"]; var_dump($a); unset($c); var_dump($a); 在第二次转储中,“key”的值显示为参考。为什么呢? 如果对普通变量而不是数组键执行相同的操作,则不会发生这种情况 我唯一的解释是,数组键通常存储为引用,只要符号表中只有一个条目,它就会在转储中显示为标量。在内部,PHP数组是哈希映射(或字典、哈希表或任何你想调用它的东西

PHP:

输出:

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

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

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


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

在内部,PHP数组是哈希映射(或字典、哈希表或任何你想调用它的东西)。甚至数字索引数组也被实现为哈希表,它是一个
zval
,与其他任何数组一样。
然而,你看到的是预期的行为,这是解释和解释的

基本上,阵列的内部外观如下:

array(1) {
  ["key"]=>
  int(23)
}
array(1) {
  ["key"]=>
  &int(23)
}
array(1) {
  ["key"]=>
  int(23)
}
对于数组,将设置
zval.type
以指示
zval
值是数组,因此将使用
zval\u value.ht
成员。
编写
$c=&$a['key']
时发生的情况是,分配给
$a['key']
zval
将被更新:
zval.refcount\u gc
将递增,
is\u ref\u gc
将被设置为1。这仅仅是因为该值没有被复制,但该值被多个变量使用:这意味着该值是一个引用。一旦你
unset($c)
refcount
递减,引用丢失,因此
is\u ref
设置为
0

现在来看一个大问题:当你使用常规的标量变量时,为什么你看不到同样的东西?这是因为数组是一个哈希表,包含自己的内部引用计数(
zval\u ptr\u dtor
)。一旦数组本身为空,它也应该被销毁。通过创建对数组值的引用,并取消设置数组,应该对
zval
进行GC'ed。但这意味着您有一个对已销毁的
zval
的引用,该引用四处浮动。
因此,数组中的
zval
也被更改为引用:可以安全地删除引用。因此,如果你要这样做:

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;
您的代码仍将按预期工作:
$foo[0]
不再存在,但
$bar
现在是对123的唯一现有引用

这只是一个非常、非常、简短且不完整的解释,但谷歌搜索PHP内部,以及内存管理如何工作,如何在内部处理引用,以及垃圾收集器如何使用
is_ref
refcount
成员来管理内存。
请特别注意内部机制,如写时复制,以及(当查看我在此处提供的第一个链接时),查找如下所示的代码段:

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

因为它在引用和数组方面处理一些奇怪的事情

是的,PHP似乎认识到您使用了一个引用,因此也用相同的引用替换数组中的值。因此,如果您更新这两个值中的一个,另一个也会更新;然而,下面的答案太好了,我不想结束它。这是非常详细的,谢谢。在阅读了PHP的内部结构之后,我仍然不明白为什么这不会发生在普通变量上,而只发生在数组中的键上。根据zval结构,内容被多次引用的每个变量都应在var_转储上显示为引用。不过,只有当我引用数组中的一个键时才会发生这种情况。@威震天:这与正在对哈希表(数组)进行的引用计数有关。因为您引用的值应该保持不变,即使在数组被GC’ed之后,数组也将保留对
zval
的引用,而不是
zval
本身的引用。实际的zval被设置为asside,并且只有在没有变量引用它的情况下才会被GC'ed(非常简单,但是你明白了)啊聪明,我没有想到这一点。现在我觉得一切都很清楚了。谢谢:)@威震天:不客气。[学究]请注意,在“帮助”部分,您被要求不要发表“谢谢”评论,而是对您认为有用的答案进行投票(接受和/或追加投票)
$ref = &$array;
foreach ($ref as $val) {}