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世纪万年历
Dec 06 PHP
php checkbox复选框值的获取与checkbox默认值输出方法
May 15 PHP
PHP编程函数安全篇
Jan 08 PHP
PHP 过滤页面中的BOM(实现代码)
Jun 29 PHP
php读取mysql中文数据出现乱码的解决方法
Aug 16 PHP
PHP使用GIFEncoder类处理gif图片实例
Jul 01 PHP
PHP过滤黑名单关键字的方法
Dec 01 PHP
laravel 5 实现模板主题功能
Mar 02 PHP
php pthreads多线程的安装与使用
Jan 19 PHP
CI框架源码解读之利用Hook.php文件完成功能扩展的方法
May 18 PHP
php实现与python进行socket通信的方法示例
Aug 30 PHP
laravel配置Redis多个库的实现方法
Apr 10 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实现时间轴函数代码
2011/10/08 PHP
PHP合并两个数组的两种方式的异同
2012/09/14 PHP
php常用字符串处理函数实例分析
2014/11/22 PHP
php根据年月获取当月天数及日期数组的方法
2016/11/30 PHP
CI(CodeIgniter)框架视图中加载视图的方法
2017/03/24 PHP
可以把编码转换成 gb2312编码lib.UTF8toGB2312.js
2007/08/21 Javascript
几个比较经典常用的jQuery小技巧
2010/03/01 Javascript
javascript实现鼠标拖动改变层大小的方法
2015/04/30 Javascript
浅谈JavaScript字符串拼接
2015/06/25 Javascript
功能强大的Bootstrap效果展示(二)
2016/08/03 Javascript
Vue.js动态组件解析
2016/09/09 Javascript
bootstrap模态框实现拖拽效果
2016/12/14 Javascript
NodeJs使用Mysql模块实现事务处理实例
2017/05/31 NodeJs
nodejs简单读写excel内容的方法示例
2018/03/16 NodeJs
Vue组件之高德地图地址选择功能的实例代码
2019/06/21 Javascript
vue动态渲染svg、添加点击事件的实现
2020/03/13 Javascript
vue实现移动端拖动排序
2020/08/21 Javascript
微信小程序获取当前时间及星期几的实例代码
2020/09/20 Javascript
swiper4实现移动端导航栏tab滑动切换
2020/10/16 Javascript
[51:17]Mski vs VGJ.S Supermajor小组赛C组 BO3 第三场 6.3
2018/06/04 DOTA
Python编程中的文件操作攻略
2015/10/16 Python
Python2.7简单连接与操作MySQL的方法
2016/04/27 Python
Python cookbook(数据结构与算法)实现优先级队列的方法示例
2018/02/18 Python
python sys.argv[]用法实例详解
2018/05/25 Python
python之cv2与图像的载入、显示和保存实例
2018/12/05 Python
基于python-opencv3的图像显示和保存操作
2019/06/27 Python
在Python中使用turtle绘制多个同心圆示例
2019/11/23 Python
python获取依赖包和安装依赖包教程
2020/02/13 Python
TensorFlow keras卷积神经网络 添加L2正则化方式
2020/05/22 Python
浅析Python 责任链设计模式
2020/09/11 Python
Python Selenium破解滑块验证码最新版(GEETEST95%以上通过率)
2021/01/29 Python
iframe在移动端的缩放的示例代码
2018/10/12 HTML / CSS
阳光体育:Sunny Sports(购买露营和远足设备)
2018/08/07 全球购物
村道德模范事迹材料
2014/08/28 职场文书
Redis安装使用RedisJSON模块的方法
2022/03/23 Redis
vue router 动态路由清除方式
2022/05/25 Vue.js