前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >php数组hashtable的巧妙设计

php数组hashtable的巧妙设计

作者头像
程序员小饭
发布2020-09-07 15:33:01
5830
发布2020-09-07 15:33:01
举报
文章被收录于专栏:golang+phpgolang+php

数组的结构

一个数组在 PHP 内核里是长什么样的呢?我们可以从 PHP 的源码里看到其结构如下:

代码语言:javascript
复制
struct _zend_array { 
    // gc 保存引用计数,内存管理相关;本文不涉及
    zend_refcounted_h gc;
    // u 储存辅助信息;本文不涉及
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar    flags,
                zend_uchar    nApplyCount,
                zend_uchar    nIteratorsCount,
                zend_uchar    consistency)
        } v;
        uint32_t flags;
    } u;
    // 用于散列函数
    uint32_t          nTableMask;   /*计算索引值*/
    // arData 指向储存元素的数组第一个 Bucket,Bucket 为统一的数组元素类型
    Bucket           *arData;
    // 已使用 Bucket 数
    uint32_t          nNumUsed;     /*已经使用的空间*/
    // 数组内有效元素个数
    uint32_t          nNumOfElements;  /*真实的元素个数*/
    // 数组总容量
    uint32_t          nTableSize;       /*用于数组扩容*/
    // 内部指针,用于遍历
    uint32_t          nInternalPointer;
    // 下一个可用数字索引
    zend_long         nNextFreeElement; /*当执行$a[]=1这样的操作的时候, $a的key就是从nNextFreeElement中取的*/
    // 析构函数
    dtor_func_t       pDestructor;
};
\color{red}{nNumUsed}
\color{red}{nNumUsed}

\color{red}{nNumOfElements}
\color{red}{nNumOfElements}

的区别: nNumUsed 指的是 arData 数组中已使用的 Bucket 数,因为数组在删除元素后只是将该元素 Bucket 对应值的类型设置为 IS_UNDEF (因为如果每次删除元素都要将数组移动并重新索引太浪费时间),而 nNumOfElements 对应的是数组中真正的元素个数。

\color{red}{nTableSize}
\color{red}{nTableSize}

数组的容量,该值为 2 的幂次方。PHP 的数组是不定长度但 C 语言的数组定长的,为了实现 PHP 的不定长数组的功能,采用了「扩容」的机制,就是在每次插入元素的时候判断 nTableSize 是否足以储存。如果不足则重新申请 2 倍 nTableSize 大小的新数组,并将原数组复制过来(此时正是清除原数组中类型为 IS_UNDEF 元素的时机)并且重新索引。

\color{red}{nNextFreeElement}
\color{red}{nNextFreeElement}

保存下一个可用数字索引,例如在 PHP 中 $a[] = 1; 这种用法将插入一个索引为 nNextFreeElement 的元素,然后 nNextFreeElement 自增 1。

接下来我们看一看bucket的结构

代码语言:javascript
复制
typedef struct _Bucket {
    // 数组元素的值
    zval              val;
    // key 通过 Time 33 算法计算得到的哈希值或数字索引
    zend_ulong        h;
    // 字符键名,数字索引则为 NULL
    zend_string      *key;
} Bucket;

插入流程图

hashtable.png

由图可知,映射表(索引数组)和数组元素(bucket数组)在同一片连续的内存中,映射表是一个长度与存储元素相同的整型数组,它默认值为 -1 ,有效值为 Bucket 数组的下标。而 HashTable->arData 指向的是这片内存中 Bucket 数组的第一个元素。

根据上图看

插入和查找 $a['foo']=1: 假如nIndex=-7(nIndex的计算方法为 nIndex = h | ht->nTableMask ),然后到索引第七个位置,首先因为保证数组插入的顺序,因为我们进行foreach的时候还要根据插入的顺序遍历出来,所以我们 第一个元素 $a['foo']=1肯定存在第0的位置,这里的key位foo,但是实际上存的是指向zend_string的指针,然后会把0的位置写到-7。当我查找的时候,先取h值或上nTableMask,得到-7,然后从索引数组里面找到-7的位置为0,然后在第0的bucket里面找,插入之后,nNumUsed和nNumOfElements也会变为1

接下来我们插入 $a[]=2

hashtable1.png

插入和查找 $a[]=2:这个时候我们看到nNextFreeElement的值为0,这个时候我们算出来nIndex的值为-8,放到了第一位置,这个时候因为没有key,h=0,然后1写到前面-8的位置,里面nNumUsed和nNumofelement+1变成2,同时nNextFreeElement变成1

接下来我们模拟一下hash冲突的情况

hashtable2.png

插入和查找 $a[‘s’]=3:假设nIndex=-7,这个时候发生了hash冲突,索引数组-7的位置变成了2,这个时候找不到foo的值,所以里面的位置是val.u2.next=0如果不相等继续往下找,通过索引数组的next值建立了一个逻辑上的链表,

冲突解决办法是拉链法,并且是头插法,最后来的放在最前面

Packed Array和Hash Array

packetArray和bucketArray.png

PHP中的所有数组都使用了Hashtable。不过在通常情况下,对于连续的、整数索引的数组(真正的数组)而言,这些hash的东西没多大意义,所以PHP 7中引入了“packed hashtables”这个概念

Packed Array: 后面是bucket,前面是索引数组,但是前面的索引数组用不着,因为packed Array是从 0123..这样递增的,所以不需要算hash值 TableMask:为-2

Hash Array:因为key,不是顺序递增的,所以算出这个值需要维护索引的数组

需要注意的是,即使是整数索引的数组,PHP也必须维持它的顺序。数组[0=>1,1=>2]和数组[1=>2,0=>1]并不是相同。packed hashtable只会作用于键递增的数组,这些数组的key之间可以有间隔,但必须总是递增的。所以如果一个元素以一个”错误“的顺序(例如逆序)插入到数组中,那么packed hashtable就不会被用到

数组的增删改查运行过程实战

我们先看一段代码

代码语言:javascript
复制
<?php
$arr = [];
echo $arr;

$arr[] = "foo";
echo $arr;

$arr[2] = "abc";
echo $arr;

$arr['a'] = "bar";
echo $arr;

$arr[] = "xyz";
echo $arr;

$arr['a'] = "foo";
echo $arr;

echo $arr['a'];

unset($arr['a']);
echo $arr;
?>

接下来我们看看对应的gdb运行过程

代码语言:javascript
复制
(gdb) b ZEND_ECHO_SPEC_CV_HANDLER
//在echo处打断点
//$arr = [];
//echo $arr;
(gdb) p *z
$1 = {value = {lval = 140737314657216, dval = 6.9533472260080157e-310, counted = 0x7ffff5a593c0, str = 0x7ffff5a593c0, arr = 0x7ffff5a593c0, obj = 0x7ffff5a593c0,
    res = 0x7ffff5a593c0, ref = 0x7ffff5a593c0, ast = 0x7ffff5a593c0, zv = 0x7ffff5a593c0, ptr = 0x7ffff5a593c0, ce = 0x7ffff5a593c0, func = 0x7ffff5a593c0, ww = {w1 = 4121269184,
      w2 = 32767}}, u1 = {v = {type = 7 '\a', type_flags = 28 '\034', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 7175}, u2 = {next = 0, cache_slot = 0, lineno = 0,
    num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}

//zval中的u1对应的type为7,说明为数组类型,所以我们直接打印p *z.value.arr

(gdb) p *z.value.arr
$2 = {gc = {refcount = 2, u = {v = {type = 7 '\a', flags = 0 '\000', gc_info = 0}, type_info = 7}}, u = {v = {flags = 18 '\022', nApplyCount = 0 '\000',
      nIteratorsCount = 0 '\000', consistency = 0 '\000'}, flags = 18}, nTableMask = 4294967294, arData = 0xd90eb4, nNumUsed = 0, nNumOfElements = 0, nTableSize = 8,
  nInternalPointer = 4294967295, nNextFreeElement = 0, pDestructor = 0x84e334 <_zval_ptr_dtor_wrapper>}
//我们观察一下数组本身,nNumUsed和nNumOfElements都为0,说明为空数组,符合我们预期,接下来我们转换一下nTableMask看一下
(gdb) p (int32_t)4294967294
$3 = -2
//nTableMask为-2,说明该数组还是Packed Array

接下来执行
$arr[] = "foo";
echo $arr;

(gdb) p *z
$1 = {value = {lval = 140737314657312, dval = 6.9533472260127587e-310, counted = 0x7ffff5a59420, str = 0x7ffff5a59420, arr = 0x7ffff5a59420, obj = 0x7ffff5a59420,
    res = 0x7ffff5a59420, ref = 0x7ffff5a59420, ast = 0x7ffff5a59420, zv = 0x7ffff5a59420, ptr = 0x7ffff5a59420, ce = 0x7ffff5a59420, func = 0x7ffff5a59420, ww = {w1 = 4121269280,
      w2 = 32767}}, u1 = {v = {type = 7 '\a', type_flags = 28 '\034', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 7175}, u2 = {next = 0, cache_slot = 0, lineno = 0,
    num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
//打印出来u1的type为7,说明为数组类型

(gdb) p *z.value.arr
$2 = {gc = {refcount = 1, u = {v = {type = 7 '\a', flags = 0 '\000', gc_info = 0}, type_info = 7}}, u = {v = {flags = 30 '\036', nApplyCount = 0 '\000',
      nIteratorsCount = 0 '\000', consistency = 0 '\000'}, flags = 30}, nTableMask = 4294967294, arData = 0x7ffff5a60a08, nNumUsed = 1, nNumOfElements = 1, nTableSize = 8,
  nInternalPointer = 0, nNextFreeElement = 1, pDestructor = 0x84e334 <_zval_ptr_dtor_wrapper>}
//打印该数组,发现nNumUsed和nNumOfElements都还为1,说明数组已经有了1个元素,nNextFreeElement也为1,说明下一个数组的空闲位置下标为1,同时nTableMask = 4294967294也即是-2,说明该数组还是Packed Array

(gdb) p z.value.arr.arData[0]
$3 = {val = {value = {lval = 140737314302208, dval = 6.95334720846829e-310, counted = 0x7ffff5a02900, str = 0x7ffff5a02900, arr = 0x7ffff5a02900, obj = 0x7ffff5a02900,
      res = 0x7ffff5a02900, ref = 0x7ffff5a02900, ast = 0x7ffff5a02900, zv = 0x7ffff5a02900, ptr = 0x7ffff5a02900, ce = 0x7ffff5a02900, func = 0x7ffff5a02900, ww = {
        w1 = 4120914176, w2 = 32767}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {next = 32767,
      cache_slot = 32767, lineno = 32767, num_args = 32767, fe_pos = 32767, fe_iter_idx = 32767, access_flags = 32767, property_guard = 32767, extra = 32767}}, h = 0, key = 0x0}
//打印第一个元素,发现key = 0x0,说明没有key,对应的u1的type为6,说明是字符串类型,所以打印该字符串
(gdb) p *z.value.arr.arData[0].val.value.str
$4 = {gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}}, h = 9223372037048267657, len = 3, val = "f"}
(gdb) p *z.value.arr.arData[0].val.value.str.val@3
$5 = "foo"
//得到结果 foo

//接下来我们执行
//$arr[2] = "abc";
//echo $arr;
(gdb) p *z
$6 = {value = {lval = 140737314657312, dval = 6.9533472260127587e-310, counted = 0x7ffff5a59420, str = 0x7ffff5a59420, arr = 0x7ffff5a59420, obj = 0x7ffff5a59420,
    res = 0x7ffff5a59420, ref = 0x7ffff5a59420, ast = 0x7ffff5a59420, zv = 0x7ffff5a59420, ptr = 0x7ffff5a59420, ce = 0x7ffff5a59420, func = 0x7ffff5a59420, ww = {w1 = 4121269280,
      w2 = 32767}}, u1 = {v = {type = 7 '\a', type_flags = 28 '\034', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 7175}, u2 = {next = 0, cache_slot = 0, lineno = 0,
    num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
//打印出来u1的type为7,说明为数组类型

(gdb) p *z.value.arr
$7 = {gc = {refcount = 1, u = {v = {type = 7 '\a', flags = 0 '\000', gc_info = 0}, type_info = 7}}, u = {v = {flags = 30 '\036', nApplyCount = 0 '\000',
      nIteratorsCount = 0 '\000', consistency = 0 '\000'}, flags = 30}, nTableMask = 4294967294, arData = 0x7ffff5a60a08, nNumUsed = 3, nNumOfElements = 2, nTableSize = 8,
  nInternalPointer = 0, nNextFreeElement = 3, pDestructor = 0x84e334 <_zval_ptr_dtor_wrapper>}
//我们可以看到此时的nNumUsed=3,而nNumOfElements=2,这是因为下标为1的位置被空出来了,
(gdb) p (int32_t)4294967294
$8 = -2
//此时该数组还为packed Array
(gdb) p z.value.arr.arData[1]
$10 = {val = {value = {lval = -4294967292, dval = -nan(0xfffff00000004), counted = 0xffffffff00000004, str = 0xffffffff00000004, arr = 0xffffffff00000004,
      obj = 0xffffffff00000004, res = 0xffffffff00000004, ref = 0xffffffff00000004, ast = 0xffffffff00000004, zv = 0xffffffff00000004, ptr = 0xffffffff00000004,
      ce = 0xffffffff00000004, func = 0xffffffff00000004, ww = {w1 = 4, w2 = 4294967295}}, u1 = {v = {type = 0 '\000', type_flags = 0 '\000', const_flags = 0 '\000',
        reserved = 0 '\000'}, type_info = 0}, u2 = {next = 32767, cache_slot = 32767, lineno = 32767, num_args = 32767, fe_pos = 32767, fe_iter_idx = 32767, access_flags = 32767,
      property_guard = 32767, extra = 32767}}, h = 18446744069414584326, key = 0x7ffff5a02a80}
//打印下标为1的数组,发现全部都是空(u1的type为0),因为没有数据
(gdb) p z.value.arr.arData[2]
$11 = {val = {value = {lval = 140737314302400, dval = 6.953347208477776e-310, counted = 0x7ffff5a029c0, str = 0x7ffff5a029c0, arr = 0x7ffff5a029c0, obj = 0x7ffff5a029c0,
      res = 0x7ffff5a029c0, ref = 0x7ffff5a029c0, ast = 0x7ffff5a029c0, zv = 0x7ffff5a029c0, ptr = 0x7ffff5a029c0, ce = 0x7ffff5a029c0, func = 0x7ffff5a029c0, ww = {
        w1 = 4120914368, w2 = 32767}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {next = 32767,
      cache_slot = 32767, lineno = 32767, num_args = 32767, fe_pos = 32767, fe_iter_idx = 32767, access_flags = 32767, property_guard = 32767, extra = 32767}}, h = 2, key = 0x0}
//打印下标为2的数组,发现key = 0x0,对应的值的u1的type为6,说明是字符串类型
(gdb) p *z.value.arr.arData[2].val.value.str
$12 = {gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}}, h = 9223372037048261771, len = 3, val = "a"}
(gdb) p *z.value.arr.arData[2].val.value.str.val@3
$13 = "abc"
//打印该字符串,得到abc

//接下来我们执行
$arr['a'] = "bar";
echo $arr;
(gdb) p *z
$14 = {value = {lval = 140737314657312, dval = 6.9533472260127587e-310, counted = 0x7ffff5a59420, str = 0x7ffff5a59420, arr = 0x7ffff5a59420, obj = 0x7ffff5a59420,
    res = 0x7ffff5a59420, ref = 0x7ffff5a59420, ast = 0x7ffff5a59420, zv = 0x7ffff5a59420, ptr = 0x7ffff5a59420, ce = 0x7ffff5a59420, func = 0x7ffff5a59420, ww = {w1 = 4121269280,
      w2 = 32767}}, u1 = {v = {type = 7 '\a', type_flags = 28 '\034', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 7175}, u2 = {next = 0, cache_slot = 0, lineno = 0,
    num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
//u1的type为7,确定是数组类型
(gdb) p *z.value.arr
$15 = {gc = {refcount = 1, u = {v = {type = 7 '\a', flags = 0 '\000', gc_info = 0}, type_info = 7}}, u = {v = {flags = 26 '\032', nApplyCount = 0 '\000',
      nIteratorsCount = 0 '\000', consistency = 0 '\000'}, flags = 26}, nTableMask = 4294967288, arData = 0x7ffff5a60b60, nNumUsed = 3, nNumOfElements = 3, nTableSize = 8,
  nInternalPointer = 0, nNextFreeElement = 3, pDestructor = 0x84e334 <_zval_ptr_dtor_wrapper>}
//打印一下nTableMask,发现是-8,说明已经变成hash Array了
(gdb) p (int32_t)4294967288
$16 = -8
(gdb) p z.value.arr.arData[2]
$17 = {val = {value = {lval = 140737314302656, dval = 6.9533472084904241e-310, counted = 0x7ffff5a02ac0, str = 0x7ffff5a02ac0, arr = 0x7ffff5a02ac0, obj = 0x7ffff5a02ac0,
      res = 0x7ffff5a02ac0, ref = 0x7ffff5a02ac0, ast = 0x7ffff5a02ac0, zv = 0x7ffff5a02ac0, ptr = 0x7ffff5a02ac0, ce = 0x7ffff5a02ac0, func = 0x7ffff5a02ac0, ww = {
        w1 = 4120914624, w2 = 32767}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {next = 4294967295,
      cache_slot = 4294967295, lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295, fe_iter_idx = 4294967295, access_flags = 4294967295, property_guard = 4294967295,
      extra = 4294967295}}, h = 9223372036854953478, key = 0x7ffff5a02a80}
//这个时候我们注意,key是有值的key = 0x7ffff5a02a80,我们可以打印一下key
(gdb) p *z.value.arr.arData[2].key
$22 = {gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}}, h = 9223372036854953478, len = 1, val = "a"}
//得到key是一个字符串,并且为a
//接下来打印对应的数组值
(gdb) p *z.value.arr.arData[2].val.value.str
$18 = {gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}}, h = 9223372037048262842, len = 3, val = "b"}
(gdb) p *z.value.arr.arData[2].val.value.str.val@3
$19 = "bar"
========================================
//接下来执行
//$arr[] = "xyz";
//echo $arr;
过程和前面一样 没什么好说的
(gdb)  p *z
$23 = {value = {lval = 140737314657312, dval = 6.9533472260127587e-310, counted = 0x7ffff5a59420, str = 0x7ffff5a59420, arr = 0x7ffff5a59420, obj = 0x7ffff5a59420,
    res = 0x7ffff5a59420, ref = 0x7ffff5a59420, ast = 0x7ffff5a59420, zv = 0x7ffff5a59420, ptr = 0x7ffff5a59420, ce = 0x7ffff5a59420, func = 0x7ffff5a59420, ww = {w1 = 4121269280,
      w2 = 32767}}, u1 = {v = {type = 7 '\a', type_flags = 28 '\034', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 7175}, u2 = {next = 0, cache_slot = 0, lineno = 0,
    num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
(gdb) p *z.value.arr
$24 = {gc = {refcount = 1, u = {v = {type = 7 '\a', flags = 0 '\000', gc_info = 0}, type_info = 7}}, u = {v = {flags = 26 '\032', nApplyCount = 0 '\000',
      nIteratorsCount = 0 '\000', consistency = 0 '\000'}, flags = 26}, nTableMask = 4294967288, arData = 0x7ffff5a60b60, nNumUsed = 4, nNumOfElements = 4, nTableSize = 8,
  nInternalPointer = 0, nNextFreeElement = 4, pDestructor = 0x84e334 <_zval_ptr_dtor_wrapper>}
(gdb) p (int32_t)4294967288
$25 = -8
(gdb) p z.value.arr.arData[3]
$26 = {val = {value = {lval = 140737314302848, dval = 6.9533472084999102e-310, counted = 0x7ffff5a02b80, str = 0x7ffff5a02b80, arr = 0x7ffff5a02b80, obj = 0x7ffff5a02b80,
      res = 0x7ffff5a02b80, ref = 0x7ffff5a02b80, ast = 0x7ffff5a02b80, zv = 0x7ffff5a02b80, ptr = 0x7ffff5a02b80, ce = 0x7ffff5a02b80, func = 0x7ffff5a02b80, ww = {
        w1 = 4120914816, w2 = 32767}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {next = 4294967295,
      cache_slot = 4294967295, lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295, fe_iter_idx = 4294967295, access_flags = 4294967295, property_guard = 4294967295,
      extra = 4294967295}}, h = 3, key = 0x0}
(gdb) p *z.value.arr.arData[3].val.value.str
$27 = {gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}}, h = 9223372037048287600, len = 3, val = "x"}
(gdb) p *z.value.arr.arData[3].val.value.str@3
$28 = {{gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}}, h = 9223372037048287600, len = 3, val = "x"}, {gc = {refcount = 32, u = {
        v = {type = 0 '\000', flags = 0 '\000', gc_info = 0}, type_info = 0}}, h = 14181744, len = 0, val = "z"}, {gc = {refcount = 4120914752, u = {v = {type = 255 '\377',
          flags = 127 '\177', gc_info = 0}, type_info = 32767}}, h = 9223372037048262314, len = 3, val = "a"}}

//接下来我们看一个问题,数组是如何根据对应的健查到对应的值的
我们以$arr['a']即p z.value.arr.arData[2]为例
首先根据h值算出nIndex  nIndex = h | tableMask
(gdb) p z.value.arr.arData[2]
$31 = {val = {value = {lval = 140737314302656, dval = 6.9533472084904241e-310, counted = 0x7ffff5a02ac0, str = 0x7ffff5a02ac0, arr = 0x7ffff5a02ac0, obj = 0x7ffff5a02ac0,
      res = 0x7ffff5a02ac0, ref = 0x7ffff5a02ac0, ast = 0x7ffff5a02ac0, zv = 0x7ffff5a02ac0, ptr = 0x7ffff5a02ac0, ce = 0x7ffff5a02ac0, func = 0x7ffff5a02ac0, ww = {
        w1 = 4120914624, w2 = 32767}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {next = 4294967295,
      cache_slot = 4294967295, lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295, fe_iter_idx = 4294967295, access_flags = 4294967295, property_guard = 4294967295,
      extra = 4294967295}}, h = 9223372036854953478, key = 0x7ffff5a02a80}
//对应的h为9223372036854953478,前面的执行结果我们可以看到tableMask为4294967288
(gdb) p (int32_t)(9223372036854953478|4294967288)
$34 = -2
//得到是第-2个位置
(gdb) p ((uint32_t*)z.value.arr.arData)[-2]
$35 = 2
//紧接着根据第-2个位置的对应关系打印出来的值正好为2
接下来我们继续执行
$arr['a'] = "foo";
echo $arr;
属于修改操作,其实这个没什么好说的,执行过程都一样,只是最后把对应的value换了一下而已
(gdb) p *z
$36 = {value = {lval = 140737314657312, dval = 6.9533472260127587e-310, counted = 0x7ffff5a59420, str = 0x7ffff5a59420, arr = 0x7ffff5a59420, obj = 0x7ffff5a59420,
    res = 0x7ffff5a59420, ref = 0x7ffff5a59420, ast = 0x7ffff5a59420, zv = 0x7ffff5a59420, ptr = 0x7ffff5a59420, ce = 0x7ffff5a59420, func = 0x7ffff5a59420, ww = {w1 = 4121269280,
      w2 = 32767}}, u1 = {v = {type = 7 '\a', type_flags = 28 '\034', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 7175}, u2 = {next = 0, cache_slot = 0, lineno = 0,
    num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0, extra = 0}}
(gdb) p *z.value.arr
$37 = {gc = {refcount = 1, u = {v = {type = 7 '\a', flags = 0 '\000', gc_info = 0}, type_info = 7}}, u = {v = {flags = 26 '\032', nApplyCount = 0 '\000',
      nIteratorsCount = 0 '\000', consistency = 0 '\000'}, flags = 26}, nTableMask = 4294967288, arData = 0x7ffff5a60b60, nNumUsed = 4, nNumOfElements = 4, nTableSize = 8,
  nInternalPointer = 0, nNextFreeElement = 4, pDestructor = 0x84e334 <_zval_ptr_dtor_wrapper>}
(gdb) p z.value.arr.arData[2]
$38 = {val = {value = {lval = 140737314302208, dval = 6.95334720846829e-310, counted = 0x7ffff5a02900, str = 0x7ffff5a02900, arr = 0x7ffff5a02900, obj = 0x7ffff5a02900,
      res = 0x7ffff5a02900, ref = 0x7ffff5a02900, ast = 0x7ffff5a02900, zv = 0x7ffff5a02900, ptr = 0x7ffff5a02900, ce = 0x7ffff5a02900, func = 0x7ffff5a02900, ww = {
        w1 = 4120914176, w2 = 32767}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {next = 4294967295,
      cache_slot = 4294967295, lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295, fe_iter_idx = 4294967295, access_flags = 4294967295, property_guard = 4294967295,
      extra = 4294967295}}, h = 9223372036854953478, key = 0x7ffff5a02a80}
(gdb) p *z.value.arr.arData[2].val.value.str
$39 = {gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}}, h = 9223372037048267657, len = 3, val = "f"}
(gdb) p *z.value.arr.arData[2].val.value.str.val@3
$40 = "foo"
接下来我们看
unset($arr['a']);
echo $arr;
(gdb) p z.value.arr.arData[2]
$44 = {val = {value = {lval = 140737314302208, dval = 6.95334720846829e-310, counted = 0x7ffff5a02900, str = 0x7ffff5a02900, arr = 0x7ffff5a02900, obj = 0x7ffff5a02900,
      res = 0x7ffff5a02900, ref = 0x7ffff5a02900, ast = 0x7ffff5a02900, zv = 0x7ffff5a02900, ptr = 0x7ffff5a02900, ce = 0x7ffff5a02900, func = 0x7ffff5a02900, ww = {
        w1 = 4120914176, w2 = 32767}}, u1 = {v = {type = 0 '\000', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 0}, u2 = {next = 4294967295,
      cache_slot = 4294967295, lineno = 4294967295, num_args = 4294967295, fe_pos = 4294967295, fe_iter_idx = 4294967295, access_flags = 4294967295, property_guard = 4294967295,
      extra = 4294967295}}, h = 9223372036854953478, key = 0x7ffff5a02a80}
我们会发现unset操作的时候,仅仅是把u1的type改为0了
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数组的结构
  • 插入流程图
  • Packed Array和Hash Array
  • 数组的增删改查运行过程实战
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档