定义:

PHP 中的数组实际上是一个有序映射。映射是一种把 values 关联到 keys 的类型。此类型在很多方面做了优化,因此可以把它当成真正的数组,或列表(向量),散列表(是映射的一种实现),字典,集合,栈,队列以及更多可能性。数组元素的值也可以是另一个数组。树形结构和多维数组也是允许的。

这是手册中对PHP数组的定义,本质上是一种键-值映射的关系,算是一种散列表(哈希表)。

到这里我不得不说,PHP内核中的神器了  HashTable

HashTable即具有双向链表的优点,PHP中的定义的变量保存在一个符号表里,而这个符号表其实就是一个HashTable,它的每一个元素都是一个zval*类型的变量。不仅如此,保存用户定义的函数、类、资源等的容器都是以HashTable的形式在内核中实现的。

因此,PHP的数组读写都可以在O(1)内完成,这是非常高效的,因此开销和C++、Java相比也就是hashtable的创建了

我们看一下PHP定义数组

<?php
$array = array();
$array["key"] = "value";

在内核中

zval* array;
array_init(array);
add_assoc_string(array, "key", "value", 1);

这样就可以了

看一下源码是怎么实现

在zend_API.h 文件中,这是一个宏定义

#define array_init(arg)                 _array_init((arg), 0 ZEND_FILE_LINE_CC)

接着看,在zend_API.c 文件中

/* Argument parsing API -- andrei */
ZEND_API int _array_init(zval *arg, uint size ZEND_FILE_LINE_DC) /* {{{ */
{
ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg)); _zend_hash_init(Z_ARRVAL_P(arg), size, NULL, ZVAL_PTR_DTOR, 0 ZEND_FILE_LINE_RELAY_CC);
Z_TYPE_P(arg) = IS_ARRAY;
return SUCCESS;
}
/* }}} */

再接着看,在zend_alloc.h文件中

#define ALLOC_HASHTABLE_REL(ht) \
(ht) = (HashTable *) emalloc_rel(sizeof(HashTable))

再接着看

ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_
FILE_LINE_DC)
{
uint i = 3;//初始化是为1<<3=8 SET_INCONSISTENT(HT_OK);
//最大个数为1>>31 = 2^31 =2147483648
if (nSize >= 0x80000000) {
/* prevent overflow */
ht->nTableSize = 0x80000000;
} else {
while ((1U << i) < nSize) {
i++;
}
ht->nTableSize = 1 << i;
} ht->nTableMask = 0; /* 0 means that ht->arBuckets is uninitialized */
ht->pDestructor = pDestructor; /* 元素的析构函数(指针) */
ht->arBuckets = (Bucket**)&uninitialized_bucket;
ht->pListHead = NULL; /* 头元素, 用于线性遍历 */
ht->pListTail = NULL; /* 尾元素, 用于线性遍历 */
ht->nNumOfElements = 0; /* HashTable中实际元素的个数 */
ht->nNextFreeElement = 0; /* 下个空闲可用位置的数字索引 */
ht->pInternalPointer = NULL; /* 内部位置指针, 会被reset, current这些遍历函数使用 */
ht->persistent = persistent;
ht->nApplyCount = 0;/* 循环遍历保护 */
ht->bApplyProtection = 1;
return SUCCESS;
}

以上是数组初始化可以参考  http://www.laruence.com/2009/08/23/1065.html

然后赋值的过程

zval* val;
MAKE_STD_ZVAL(val);
ZVAL_STRING(val, "value", 0);
zend_hash_add(h, "key", 4, &val, sizeof(zval*), NULL);

内核为我们提供了方便的宏来管理数组

ZEND_API int add_index_long(zval *arg, ulong idx, long n);
ZEND_API int add_index_null(zval *arg, ulong idx);
ZEND_API int add_index_bool(zval *arg, ulong idx, int b);
ZEND_API int add_index_resource(zval *arg, ulong idx, int r);
ZEND_API int add_index_double(zval *arg, ulong idx, double d);
ZEND_API int add_index_string(zval *arg, ulong idx, const char *str, int duplicate);
ZEND_API int add_index_stringl(zval *arg, ulong idx, const char *str, uint length, int duplicate);
ZEND_API int add_index_zval(zval *arg, ulong index, zval *value); ZEND_API int add_next_index_long(zval *arg, long n);
ZEND_API int add_next_index_null(zval *arg);
ZEND_API int add_next_index_bool(zval *arg, int b);
ZEND_API int add_next_index_resource(zval *arg, int r);
ZEND_API int add_next_index_double(zval *arg, double d);
ZEND_API int add_next_index_string(zval *arg, const char *str, int duplicate);
ZEND_API int add_next_index_stringl(zval *arg, const char *str, uint length, int duplicate); ZEND_API int add_assoc_long_ex(zval *arg, const char *key, uint key_len, long n);
ZEND_API int add_assoc_null_ex(zval *arg, const char *key, uint key_len);
ZEND_API int add_assoc_bool_ex(zval *arg, const char *key, uint key_len, int b);
ZEND_API int add_assoc_resource_ex(zval *arg, const char *key, uint key_len, int r);
ZEND_API int add_assoc_double_ex(zval *arg, const char *key, uint key_len, double d);
ZEND_API int add_assoc_string_ex(zval *arg, const char *key, uint key_len, char *str, int duplicate);
ZEND_API int add_assoc_stringl_ex(zval *arg, const char *key, uint key_len, char *str, uint length, int duplicate);
ZEND_API int add_assoc_zval_ex(zval *arg, const char *key, uint key_len, zval *value); #define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key)+1, __n)
#define add_assoc_null(__arg, __key) add_assoc_null_ex(__arg, __key, strlen(__key) + 1)
#define add_assoc_bool(__arg, __key, __b) add_assoc_bool_ex(__arg, __key, strlen(__key)+1, __b)
#define add_assoc_resource(__arg, __key, __r) add_assoc_resource_ex(__arg, __key, strlen(__key)+1, __r)
#define add_assoc_double(__arg, __key, __d) add_assoc_double_ex(__arg, __key, strlen(__key)+1, __d)
#define add_assoc_string(__arg, __key, __str, __duplicate) add_assoc_string_ex(__arg, __key, strlen(__key)+1, __str, __duplicate)
#define add_assoc_stringl(__arg, __key, __str, __length, __duplicate) add_assoc_stringl_ex(__arg, __key, strlen(__key)+1, __str, __len
gth, __duplicate)
#define add_assoc_zval(__arg, __key, __value) add_assoc_zval_ex(__arg, __key, strlen(__key)+1, __value)

具体实现

$arr[] = NULL;  add_next_index_null(arr);
$arr[] = 42; add_next_index_long(arr, 42);
$arr[] = true; add_next_index_bool(arr, 1);
$arr[] = 3.14; add_next_index_double(arr, 3.14);
$arr[] = 'foo'; add_next_index_string(arr, "foo");
$arr[] = $var; add_next_index_zval(arr, zval); $arr[0] = NULL; add_index_null(arr, 0);
$arr[1] = 42; add_index_long(arr, 1, 42);
$arr[2] = true; add_index_bool(arr, 2, 1);
$arr[3] = 3.14; add_index_double(arr, 3, 3.14);
$arr[4] = 'foo'; add_index_string(arr, 4, "foo", 1);
$arr[5] = $var; add_index_zval(arr, 5, zval); $arr["abc"] = NULL; add_assoc_null(arr, "abc");
$arr["def"] = 42; add_assoc_long(arr, "def", 42);
$arr["ghi"] = true; add_assoc_bool(arr, "ghi", 1);
$arr["jkl"] = 3.14 add_assoc_double(arr, "jkl", 3.14);
$arr["mno"]="foo" add_assoc_string(arr, "mno", "foo", 1");
$arr["pqr"] = $var; add_assoc_zval(arr, "pqr", zval);

扩展中测试

PHP_FUNCTION(array_test1)
{
zval *value;
zval *element;
char *s="This is a test";
char *key="a";
MAKE_STD_ZVAL(element);
MAKE_STD_ZVAL(value);
array_init(value);
ZVAL_STRING(element,s,strlen(s));
zend_hash_update(value->value.ht,key,strlen(key)+1,(void*)&element,sizeof(zval*),NULL);
ZEND_SET_SYMBOL(EG(active_symbol_table),"siren",value);
} PHP_FUNCTION(array_test2){
char* str;
zval* subarray;
array_init(return_value);
array_init(subarray);
add_next_index_string(return_value, "test", 1);
str = estrdup("This is a test");
add_next_index_string(return_value, str, 0);
add_assoc_double(return_value, "double", 3.14);
add_next_index_string(subarray, "hello", 1);
add_assoc_zval(return_value, "xsubarray", subarray);
}

深入PHP内核之数组的更多相关文章

  1. php内核之数组

    1.zend_hash_num_elements 获取数组元素个数.宏定义如下: #define zend_hash_num_elements(ht) \ (ht)->nNumOfElement ...

  2. [翻译] Linux 内核中的位数组和位操作

    目录 Linux 内核里的数据结构 原文链接与说明 Linux 内核中的位数组和位操作 位数组声明 体系结构特定的位操作 通用位操作 链接 Linux 内核里的数据结构 原文链接与说明 https:/ ...

  3. Netlink 内核实现分析 2

    netlink 应用层如何创建socket 应用层通过socket()系统调用创建Netlink套接字,socket系统调用的第一个参数可以是AF_NETLINK或PF_NETLINK(在Linux系 ...

  4. kernel编程规范

    1. 制表符8个空格2. 每行最长80字符3. 代码块的{放在首行,但是函数的{放在次行4. 只有一行的if块,不加{}5. 不在()前后加空格6. 正常关键字后加一个空格,if, switch, c ...

  5. select、poll、epoll三组IO复用

    int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,struct timeval* timeout)//其中n ...

  6. Linux中的IO复用接口简介(文件监视?)

    I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux ...

  7. Linux Platform驱动模型(二) _驱动方法

    在Linux设备树语法详解和Linux Platform驱动模型(一) _设备信息中我们讨论了设备信息的写法,本文主要讨论平台总线中另外一部分-驱动方法,将试图回答下面几个问题: 如何填充platfo ...

  8. Linux Platform驱动模型(二) _驱动方法【转】

    转自:http://www.cnblogs.com/xiaojiang1025/archive/2017/02/06/6367910.html 在Linux设备树语法详解和Linux Platform ...

  9. 嵌入式:UCOSIII的使用(17.01.24补充)

    0.一些移植.系统相关 OS_CFG_APP.H /* --------------------- MISCELLANEOUS ------------------ */ #define OS_CFG ...

随机推荐

  1. 重载 UINavigationController 设置左侧返回按钮的文字为图片

    UINavigationController 导航栏控制器的左侧返回按钮如果需要设置成图片,仅使用系统的是无法实现的,需要重载系统的导航栏控制器,在控制器推出之前替换掉leftBarButtonIte ...

  2. unity3d-地图制作之光照贴图Lightmapping

    今天无聊随便翻看了暗黑战神的场景资源,发现了一个以前没怎么注意的静态场景优化问题. 什么是静态场景,也就是说这个场景是不会变化.比如MMO游戏中选择人物的场景. 就拿默认的暗黑战神的选择人物场景来看, ...

  3. org-mode入门教程

    org-mode 入门教程By Z.H. Fu 切问录 www.fuzihao.org org-mode 入门教程 org-mode是Emacs提供的一个强大的编辑模式,可以用于做会议笔记以及制作各种 ...

  4. acd Convex(求面积)

    Problem Description We have a special convex that all points have the same distance to origin point. ...

  5. VMware vCenter中, 如何辩认虚机上Raw Device Mapping过了的一块物理磁盘?

    比如说, 我们有一套VMware的环境, 其中有一台运行者ESXi的主机, 其上有十块SAS盘. 这十块盘中的五块盘被RDM到一台虚机上了. 假设你发现有添加多了一块盘, 你想移除掉, 但是5块盘其中 ...

  6. Binary Tree Inorder Traversal leetcode java

    题目: Given a binary tree, return the inorder traversal of its nodes' values. For example: Given binar ...

  7. 基于json的jquery地区联动探索

    贴一个基于jquery联动的代码,相信这样的代码有一大把,就当是jquery的练手吧, 写这个东西的初衷是来之于新浪微博,在新浪微博帐号设置里面有个地区的选项,使用js写的,想把它的代码给截获下来,可 ...

  8. 申请红帽企业版Linux开发者订阅

    导读 注册成为开发者计划的成员现在可以得到一套免费的 Red Hat Enterprise Linux 许可证,RHEL 开发套件将为程序员提供一个构建企业应用的稳定发展平台.红帽开发订阅成员还可以免 ...

  9. MVC4.0网站发布

    一.VS2010下MVC4.0项目的发布 首先,生成网站发布文件. 第一步,"右击"要发布的MVC4.0项目,选择"发布(B)..."选项,如图: 第二步,在& ...

  10. vim配置 高亮+自动缩进+行号+折叠+优化

    一:修改 .vimrc即可 二: set nocompatible " 关闭 vi 兼容模式syntax on " 自动语法高亮colorscheme molokai " ...