PHP7数组源码解析
数组是PHP中非常强大、灵活的一种数据类型.本次分享主要介绍一下数组的底层结构;如何对数组进行查找、写入、扩容操作;以及遇到Hash冲突时是怎样巧妙处理的.
1.zval结构体
在讲它的底层实现之前,我们先了解一下变量的结构zval.(为了减少篇幅,不重要的部分用…代替)
//本文源码均基于php7.1.26
struct _zval_struct {
zend_value value;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type,
...
} v;
uint32_t type_info;
} u1;
union {
uint32_t next;
...
} u2;
};
typedef union _zend_value {
zend_long lval;
double dval;
zend_string *str;
zend_array *arr;
...
} zend_value;
u1联合体中的type用来控制变量类型,类型之间的对应关系如下:
/* regular data types */
#define IS_UNDEF 0
#define IS_NULL 1
#define IS_FALSE 2
#define IS_TRUE 3
#define IS_LONG 4
#define IS_DOUBLE 5
#define IS_STRING 6
#define IS_ARRAY 7
#define IS_OBJECT 8
#define IS_RESOURCE 9
#define IS_REFERENCE 10
当type为7时变量类型为数组
u2联合体中的next用来解决hash冲突
2.HashTable的巧妙设计
PHP 数组的底层实现是散列表(也叫 hashTable ),散列表是根据键(Key)直接访问内存存储位置的数据结构,它的 key - value 之间存在一个映射函数,可以根据 key 通过映射函数得到的散列值直接索引到对应的 value 值,无需通过关键字比较,在理想情况下,不考虑散列冲突,散列表的查找效率是非常高的,时间复杂度是 O (1)。
从源码中我们可以看到 zend_array 的结构如下:
typedef struct _zend_array HashTable;
struct _zend_array {
zend_refcounted_h gc;
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;
Bucket *arData;
uint32_t nNumUsed;
uint32_t nNumOfElements;
uint32_t nTableSize;
uint32_t nInternalPointer;
zend_long nNextFreeElement;
dtor_func_t pDestructor;
};
主要字段介绍:
- gc: 垃圾回收相关,记录了引用计数等信息.
- nTableSize: 数组占用的长度,2的n次方(最小为8).
- nTableMask :这个值在散列函数根据key的hash code映射元素的存储位置时用到,它的值实际就是nTableSize的负