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”的值显示为参考。这是为什么? 如果我使用普通变量而不是数组键执行相同操作,则不会发生这种情况。
我唯一的解释是数组键通常存储为引用,只要符号表中只有一个条目,它就会在转储中显示为标量。
答案 0 :(得分:1)
在内部,PHP数组是散列图(或字典,或HashTables或任何你想要调用它的东西)。即使是数字索引的数组也可以像哈希表一样实现zval
,就像其他任何一样。
但是,您所看到的是预期的行为,解释为both here和here。
基本上,你的数组内部是什么样的:
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']
时会发生什么情况:分配给zval
的{{1}}将会更新:$a['key']
将会增加,zval.refcount__gc
将设置为1.仅仅因为未复制值,但该值由多于1个变量使用:意味着此值是引用。 is_ref__gc
后,unset($c);
会递减,参考会丢失,因此refcount
设置为is_ref
。
现在换一个大问题:当你使用常规的标量变量时,为什么不看同样的事情?好吧,那是因为一个数组是一个HashTable,它有自己的内部引用计数(0
)。一旦数组本身为空,它也应该被销毁。通过创建对数组值的引用,并取消设置数组,zval_ptr_dtor
应该是GC。但这意味着你可以参考被淹没的zval
。{
因此,数组中的zval
也会更改为引用:可以安全地删除引用。所以,如果你这样做:
zval
您的代码仍将按预期运行:$foo = array(123);
$bar = &$foo[0];
unset($foo[0]);
echo $bar, PHP_EOL;
不再存在,但$foo[0]
现在是123的唯一现有引用。
这只是一个非常,真实,简短和不完整的解释,但谷歌PHP内部,内存管理如何工作,如何在内部处理引用,以及垃圾收集器如何使用$bar
和{ {1}}成员管理记忆
特别注意内部机制,如copy-on-write和(当查看我在这里提供的第一个链接时),查找看起来像这样的代码片段:
is_ref
因为它涉及引用和数组方面的一些奇怪之处。