zoukankan      html  css  js  c++  java
  • PHP内核之旅-5.强大的数组

    PHP 内核之旅系列

    一、数组的内部结构

    1.底层实现为散列表(HashTable,也称作哈希表)

    2.散列表的概念:

    是根据关键码值(Key value)而直接进行访问的数据结构。通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。复杂度为O(1)。

    文件路径endzend_types.h

    _zend_array结构:

     1 typedef struct _zend_array zend_array;
     2 typedef struct _zend_array HashTable;
     3 
     4 struct _zend_array {
     5     zend_refcounted_h gc;
     6     union {
     7         struct {
     8             ZEND_ENDIAN_LOHI_4(
     9                 zend_uchar    flags,
    10                 zend_uchar    nApplyCount,
    11                 zend_uchar    nIteratorsCount,
    12                 zend_uchar    consistency)
    13         } v;
    14         uint32_t flags;
    15     } u;
    16     uint32_t          nTableMask;
    17     Bucket           *arData;
    18     uint32_t          nNumUsed;
    19     uint32_t          nNumOfElements;
    20     uint32_t          nTableSize;
    21     uint32_t          nInternalPointer;
    22     zend_long         nNextFreeElement;
    23     dtor_func_t       pDestructor;
    24 };

    zend_array和HashTable结构相同 

    arData:散列表中保存存储元素的数组,其内存是连续的,arData指向数组的起始位置,其内存连续。

    nTableSize:数组的总容量,可以容纳的元素数,大小是2的幂次方,最小为8

    nTableMask: 映射元素的存储位置用到,nTableSize的负数

    nNumUsed: 数组当前使用的Bucket数,删除元素时,不会将其从数组中移除,将这个元素的类型标为IS_UNDEF,当数组容量超限,扩容时才会删除。

    nNumOfElements:数组中有效元素的位置

    nNextFreeElement:下一个数值的索引

    pDestructor:删除或覆盖数组中的某个元素时,则调用此函数对旧元素进行处理

    u:辅助作用

    Bucket结构:

    1 typedef struct _Bucket {
    2     zval              val;
    3     zend_ulong        h;                /* hash value (or numeric index)   */
    4     zend_string      *key;              /* string key or NULL for numerics */
    5 } Bucket;

     val: 具体的value

     h: key的hash值,或者数值索引

    *key: 存储元素的key,如果元素是数值索引则为NULL

    二、数组的基本实现

    散列函数:将元素进行hash运算后的值,对数组大小取模之后的值(下标:0~7)分配到中间映射表

    中间映射表:元素和下标的映射关系表。可以通过arData向前访问到

    元素数组:实际存储元素的数组,按照下标有序存储元素

    三、数组的初始化

    endzend_hash.c

     1 ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
     2 {
     3     GC_REFCOUNT(ht) = 1;
     4     GC_TYPE_INFO(ht) = IS_ARRAY | (persistent ? 0 : (GC_COLLECTABLE << GC_FLAGS_SHIFT));
     5     ht->u.flags = (persistent ? HASH_FLAG_PERSISTENT : 0) | HASH_FLAG_APPLY_PROTECTION | HASH_FLAG_STATIC_KEYS;
     6     ht->nTableMask = HT_MIN_MASK;
     7     HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
     8     ht->nNumUsed = 0;
     9     ht->nNumOfElements = 0;
    10     ht->nInternalPointer = HT_INVALID_IDX;
    11     ht->nNextFreeElement = 0;
    12     ht->pDestructor = pDestructor;
    13     ht->nTableSize = zend_hash_check_size(nSize);
    14 }

    endzend_API.c

    1 ZEND_API int _array_init(zval *arg, uint32_t size ZEND_FILE_LINE_DC) /* {{{ */
    2 {
    3     ZVAL_NEW_ARR(arg);
    4     _zend_hash_init(Z_ARRVAL_P(arg), size, ZVAL_PTR_DTOR, 0 ZEND_FILE_LINE_RELAY_CC);
    5     return SUCCESS;
    6 }

     四、插入

    插入时首先会检查数组已经分配存储空间,初始化时没有实际分配arData的内存,第一次插入时才会根据nTableSize的大小分配,分配完以后会把HashTable->u.flags打上HASH_FLAG_INITIALIZED掩码,下次插入时发现已经分配了就不会再重复操作。

     1 static zend_always_inline void zend_hash_check_init(HashTable *ht, int packed)
     2 {
     3     HT_ASSERT_RC1(ht);
     4     if (UNEXPECTED(!((ht)->u.flags & HASH_FLAG_INITIALIZED))) {
     5         zend_hash_real_init_ex(ht, packed);
     6     }
     7 }
     8 
     9 #define CHECK_INIT(ht, packed) 
    10     zend_hash_check_init(ht, packed)

    参考资料:

    http://www.php-internals.com/

    PHP7内核剖析


    作  者: Jackson0714
    出  处:http://www.cnblogs.com/jackson0714/
    关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教!
    版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
    特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信
    声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力!

  • 相关阅读:
    Codeforces 451A Game With Sticks
    POJ 3624 Charm Bracelet
    POJ 2127 Greatest Common Increasing Subsequence
    POJ 1458 Common Subsequence
    HDU 1087 Super Jumping! Jumping! Jumping!
    HDU 1698
    HDU 1754
    POJ 1724
    POJ 1201
    CSUOJ 1256
  • 原文地址:https://www.cnblogs.com/jackson0714/p/php5.html
Copyright © 2011-2022 走看看