看到一道php笔试题目,如下:
$str = "hello";
//输出以下结果
echo $str[1];
echo count($str);
这道题是想了解开发人员对php内核的zvalue数据结构和count函数熟悉程度。初学者一般是背答案的,下面我们一起来分析下。
看看php源码目录php/Zend/Zend.h
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;
_zvalue_value是表示php变量的联合体,当对一个变量赋值字符串时,结构体str的val指针指向字符串,len表示长度。
那么,$str[1]就表示为c语言的str[1],即是字符e。
看看php源码目录php/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;
case IS_OBJECT: {
#ifdef HAVE_SPL
zval *retval;
#endif
/* first, we check if the handler is defined */
if (Z_OBJ_HT_P(array)->count_elements) {
RETVAL_LONG(1);
if (SUCCESS == Z_OBJ_HT(*array)->count_elements(array, &Z_LVAL_P(return_value) TSRMLS_CC)) {
return;
}
}
#ifdef HAVE_SPL
/* if not and the object implements Countable we call its count() method */
if (Z_OBJ_HT_P(array)->get_class_entry && instanceof_function(Z_OBJCE_P(array), spl_ce_Countable TSRMLS_CC)) {
zend_call_method_with_0_params(&array, NULL, NULL, "count", &retval);
if (retval) {
convert_to_long_ex(&retval);
RETVAL_LONG(Z_LVAL_P(retval));
zval_ptr_dtor(&retval);
}
return;
}
#endif
}
default:
RETURN_LONG(1);
break;
}
}
|
由此可看到,count函数对string处理直接进入default分支,返回1。
说说对数组使用count函数,count($array)直接读取HashTable结构体的nNumberOfElements,时间复杂度为0(1),就算在for阈值判断时使用没多大影响,就是看起来不舒服了。。。
static int php_count_recursive(zval *array, long mode TSRMLS_DC)
{
/*省略非关键代码*/
cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
return cnt;
}
zend/zend_hash.c
ZEND_API int zend_hash_num_elements(const HashTable *ht)
{
IS_CONSISTENT(ht);
return ht->nNumOfElements;
}