PHP 内核之旅系列

一、数组的内部结构

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

2.散列表的概念:

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

文件路径\Zend\zend_types.h

_zend_array结构:

 typedef struct _zend_array 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;
};

zend_array和HashTable结构相同

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

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

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

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

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

nNextFreeElement:下一个数值的索引

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

u:辅助作用

Bucket结构:

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

val: 具体的value

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

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

二、数组的基本实现

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

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

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

三、数组的初始化

\Zend\zend_hash.c

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

\Zend\zend_API.c

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

四、插入

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

 static zend_always_inline void zend_hash_check_init(HashTable *ht, int packed)
{
HT_ASSERT_RC1(ht);
if (UNEXPECTED(!((ht)->u.flags & HASH_FLAG_INITIALIZED))) {
zend_hash_real_init_ex(ht, packed);
}
} #define CHECK_INIT(ht, packed) \
zend_hash_check_init(ht, packed)

参考资料:

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

PHP7内核剖析

作  者:
Jackson0714

出  处:http://www.cnblogs.com/jackson0714/

关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教!

版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信

声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力!

PHP内核之旅-5.强大的数组的更多相关文章

  1. PHP内核之旅-2.SAPI中的Cli

    PHP 内核之旅系列 PHP内核之旅-1.生命周期 PHP内核之旅-2.SAPI中的Cli 一.SAPI是什么? 1.1 理解SAPI (1)SAPI是PHP框架的接口层.有很多种服务器的SAPI的实 ...

  2. PHP内核之旅-3.变量

    PHP 内核之旅系列 PHP内核之旅-1.生命周期 PHP内核之旅-2.SAPI中的Cli PHP内核之旅-3.变量 一.弱类型语言 php是弱类型语言.一个变量可以表示任意数据类型. php强大的一 ...

  3. PHP内核之旅-6.垃圾回收机制

    回收PHP 内核之旅系列 PHP内核之旅-1.生命周期 PHP内核之旅-2.SAPI中的Cli PHP内核之旅-3.变量 PHP内核之旅-4.字符串 PHP内核之旅-5.强大的数组 PHP内核之旅-6 ...

  4. PHP内核之旅-4.可变长度的字符串

    PHP 内核之旅系列 PHP内核之旅-1.生命周期 PHP内核之旅-2.SAPI中的Cli PHP内核之旅-3.变量 PHP内核之旅-4.字符串 PHP内核之旅-5.强大的数组 PHP内核之旅-6.垃 ...

  5. JAVA之旅(三)——数组,堆栈内存结构,静态初始化,遍历,最值,选择/冒泡排序,二维数组,面向对象思想

    JAVA之旅(三)--数组,堆栈内存结构,静态初始化,遍历,最值,选择/冒泡排序,二维数组,面向对象思想 我们继续JAVA之旅 一.数组 1.概念 数组就是同一种类型数据的集合,就是一个容器 数组的好 ...

  6. MOOC Linux内核之旅小结【转】

    转自:https://blog.csdn.net/titer1/article/details/45345123 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csd ...

  7. Linux内核之旅

    http://www.kerneltravel.net/ Linux内核之旅 Linux Kernel Travel

  8. PHP强大的数组函数

    <?php/** * Created by PhpStorm. * User: 63448 * Date: 2018/5/6 * Time: 21:56 */echo "<h3& ...

  9. 【Swift学习】Swift编程之旅---集合类型之数组(六)

    swift提供了3种主要的集合类型,array,set,dictionary.本节介绍array. 数组是存储有序的相同类型的集合,相同的值可以多次出现在不同的位置. 注意: swift的Array类 ...

随机推荐

  1. openfire推送离线聊天信息的插件

    插件说明 在手机的聊天应用中,经常出现的一个需求就是把用户的离线消息通过推送系统推送到用户的手机上,为了实现这个功能,本人就开发了本插件,这个openfire 插件是拦截了发给openfire用户的离 ...

  2. Android 框架炼成 教你如何写组件间通信框架EventBus

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/41096639 ,本文出自:[张鸿洋的博客] 1.概述 关于Eventbus的介绍 ...

  3. Dreamweaver无法启动:xml parsing fatal error..Designer.xml错误解决方法

    xml parsing fatal error:Invalid document structure,line:1,file:C:\Documents and Settings\Administrat ...

  4. Goroutine陷阱

    Go在语言层面通过Goroutine与channel来支持并发编程,使并发编程看似变得异常简单,但通过最近一段时间的编码,越来越觉得简单的东西,很容易会被滥用.Java的标准库也让多线程编程变得简单, ...

  5. 【codeforces 698C】LRU

    题目链接: http://codeforces.com/problemset/problem/698/C 题目大意: n个物品,k个格子,第i个物品每次被选取的概率为$p_{i}$,如果格子里没有该物 ...

  6. 基于 Maven 的多模块 Java ( Spring ) 项目构建

    索引: 开源Spring解决方案--lm.solution 参看代码 GitHub: solution/pom.xml pojo/pom.xml mapper/pom.xml common/pom.x ...

  7. 深入理解数据库磁盘存储(Disk Storage)

    数据库管理系统将数据存储在磁盘.磁带以及其他的裸设备上,虽然这些设备的访问速度相比内存慢很多,但其非易失性和大容量的特点使他们成为数据存储的不二之选. 本文主要讨论大型数据库产品的磁盘存储内部结构,这 ...

  8. 如何在招聘中考核.NET架构师

    .NET架构师招聘不如JAVA那么顺利,可以搜索到的.NET架构师可以说是凤毛菱角.当然好的架构师都是需要长期观察和挖角才能得手,如何去招聘到合适的.NET架构师可能是摆在所有求贤者面前的难题.这里的 ...

  9. Java后端框架之Spring Boot详解,文末有Java分布式实战项目视频可取

    在 Java 后端框架繁荣的今天,Spring 框架无疑是最最火热,也是必不可少的开源框架,更是稳坐 Java 后端框架的龙头老大. 用过 Spring 框架的都知道 Spring 能流行是因为它的两 ...

  10. CentOS 本地和网络yum源简单说明及配置

    1.简述 Yellow dog Updater, Modified由Duke University团队,修改Yellow Dog Linux的Yellow Dog Updater开发而成,是一个基于R ...