PHP源代码数组统计count分析


Posted in PHP onAugust 02, 2011

zend给php的所有变量都用结构的方式去保存,而字符串的保存和数组的保存也是不同的,数组采用的是hash表的方式去保存(大家知道hash保存的地址有效的减少冲突-hash散列表的概念你懂的),而在php中的结构体上表现如下:

//文件1:zend/zend.h 
/* 
* zval 
*/ 
typedef struct _zval_struct zval; 
... 
typedef union _zvalue_value { 
long lval; /* long value */ 
double dval; /* double value */ 
struct { 
char *val; 
int len; 
} str; 
HashTable *ht; /* hash table value */ 
zend_object_value obj; 
} zvalue_value; struct _zval_struct { 
/* Variable information */ 
zvalue_value value; /* value */ 
zend_uint refcount__gc; 
zend_uchar type; /* active type */ 
zend_uchar is_ref__gc; 
}; 
//hash表的结构如下 
//文件2:zend/zend_hash.h 
typedef struct _hashtable { 
uint nTableSize; 
uint nTableMask; 
uint nNumOfElements; 
ulong nNextFreeElement; 
Bucket *pInternalPointer; /* Used for element traversal */ 
Bucket *pListHead; 
Bucket *pListTail; 
Bucket **arBuckets; 
dtor_func_t pDestructor; 
zend_bool persistent; 
unsigned char nApplyCount; 
zend_bool bApplyProtection; 
#if ZEND_DEBUG 
int inconsistent; 
#endif 
} 
HashTable;

一般的变量(字符串)在使用strlen获取长度的时候,其实获取的就是zvalue_value.str这个结构中的len属性,效率上O(1)次,特别说明的一点是:strlen在php中并没有核心的实现,而是在使用了zend中的宏定义来获取:
//文件3:zend/zend_operators.php 
#define Z_STRLEN(zval) (zval).value.str.len 
... 
#define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p) 
... 
#define Z_STRLEN_PP(zval_pp) Z_STRLEN(**zval_pp)

而对于数组的count操作,其实有两种结果,在count 的api中也提到了第二个参数mode《http://www.php.net/manual/en/function.count.php》,这个mode参数指明了,是否需要重新统计,而它的重新统计将会遍历一次数组,效率上是O(N)[N:长度],默认情况下是不重新统计,那这个时候将会直接输出hashtable中的nNumOfElements,此时的效率也是O(1)次:count代码如下:
//文件4:ext/standard/array.c 
PHP_FUNCTION(count) 
{ 
zval *array; 
long mode = COUNT_NORMAL; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) { 
return; 
} 
switch (Z_TYPE_P(array)) { 
case IS_NULL: 
RETURN_LONG(0); 
break; 
case IS_ARRAY: 
RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC)); 
break; 
..... 
//php_count_recursive的实现 
static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */ 
{ 
long cnt = 0; 
zval **element; 
if (Z_TYPE_P(array) == IS_ARRAY) { 
//错误处理 
if (Z_ARRVAL_P(array)->nApplyCount > 1) { 
php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); 
return 0; 
} 
//通过zend_hash_num_elements直接获得长度 
cnt = zend_hash_num_elements(Z_ARRVAL_P(array)); 
//如果指定了需要重新统计,则会进入一次循环统计 
if (mode == COUNT_RECURSIVE) { 
HashPosition pos; 
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos); 
zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS; 
zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos) 
) { 
Z_ARRVAL_P(array)->nApplyCount++; 
cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC); 
Z_ARRVAL_P(array)->nApplyCount--; 
} 
} 
} 
return cnt; 
} 
//文件5:zend/zend_hash.c 
//zend_hash_num_elements的实现 
ZEND_API int zend_hash_num_elements(const HashTable *ht) 
{ 
IS_CONSISTENT(ht); 
return ht->nNumOfElements; 
}
PHP 相关文章推荐
php+oracle 分页类
Oct 09 PHP
php smarty模版引擎中变量操作符及使用方法
Dec 11 PHP
在PHP中养成7个面向对象的好习惯
Jan 28 PHP
Can't create/write to file 'C:\WINDOWS\TEMP\...MYSQL报错解决方法
Jun 30 PHP
使用php清除bom示例
Mar 03 PHP
ThinkPHP实现跨模块调用操作方法概述
Jun 20 PHP
php中http与https跨域共享session的解决方法
Dec 20 PHP
php实现阿拉伯数字和罗马数字相互转换的方法
Apr 17 PHP
Yii CGridView用法实例详解
Jul 12 PHP
PHP中file_put_contents追加和换行的实现方法
Apr 01 PHP
php合并数组并保留键值的实现方法
Mar 12 PHP
tp5.1 框架join方法用法实例分析
May 26 PHP
linux下为php添加curl扩展的方法
Jul 29 #PHP
php中修改浏览器的User-Agent来伪装你的浏览器和操作系统
Jul 29 #PHP
php 判断访客是否为搜索引擎蜘蛛的函数代码
Jul 29 #PHP
php.ini中date.timezone设置分析
Jul 29 #PHP
PHP调用Webservice实例代码
Jul 29 #PHP
php和数据库结合的一个简单的web实例 代码分析 (php初学者)
Jul 28 #PHP
一个典型的PHP分页实例代码分享
Jul 28 #PHP
You might like
聊天室php&mysql(五)
2006/10/09 PHP
PHP5常用函数列表(分享)
2013/06/07 PHP
php输入数据统一类实例
2015/02/23 PHP
php单例模式的简单实现方法
2016/06/10 PHP
PHP框架自动加载类文件原理详解
2017/06/06 PHP
PHP区块查询实现方法分析
2018/05/12 PHP
js中if语句的几种优化代码写法
2011/03/12 Javascript
javascript中的面向对象
2017/03/30 Javascript
Vue中建立全局引用或者全局命令的方法
2017/08/21 Javascript
vue中实现在外部调用methods的方法(推荐)
2018/02/08 Javascript
angularjs使用gulp-uglify压缩后执行报错的解决方法
2018/03/07 Javascript
JS实现关键词高亮显示正则匹配
2018/06/22 Javascript
NodeJS实现同步的方法
2019/03/02 NodeJs
Vue 实现分页与输入框关键字筛选功能
2020/01/02 Javascript
vue项目实现设置根据路由高亮对应的菜单项操作
2020/08/06 Javascript
nginx部署多个vue项目的方法示例
2020/09/06 Javascript
[43:48]Ti4正赛第一天 VG vs NEWBEE 2
2014/07/19 DOTA
python条件和循环的使用方法
2013/11/01 Python
python字典多条件排序方法实例
2014/06/30 Python
python利用微信公众号实现报警功能
2018/06/10 Python
Python自动抢红包教程详解
2019/06/11 Python
python多线程共享变量的使用和效率方法
2019/07/16 Python
Django之模板层的实现代码
2019/09/09 Python
python+selenium 简易地疫情信息自动打卡签到功能的实现代码
2020/08/22 Python
G-Form护具官方网站:美国运动保护装备
2019/09/04 全球购物
旅游管理毕业生自荐书
2014/02/02 职场文书
给老师的检讨书
2014/02/11 职场文书
名企HR怎样看待求职信
2014/02/23 职场文书
材料专业毕业生求职信
2014/02/26 职场文书
公司大门门卫岗位职责
2014/06/11 职场文书
师德师风自我评价范文
2014/09/11 职场文书
2019年12月24日平安夜祝福语集锦
2019/12/24 职场文书
为什么不建议在go项目中使用init()
2021/04/12 Golang
两行代码解决Jupyter Notebook中文不能显示的问题
2021/04/24 Python
MySQL InnoDB ReplicaSet(副本集)简单介绍
2021/04/24 MySQL
利用uni-app生成微信小程序的踩坑记录
2022/04/05 Javascript