您好,欢迎访问一九零五行业门户网

PHP扩展开发-数组的使用以及HashTable简介_PHP教程

1      数组
本节我们讲一下php的数组,在php中,数组使用hashtable实现的。本节中我们先详细的介绍一下hashtable,然后再讲讲如何使用hasttable
1.1     变长结构体
所谓的变长结构体,其实是我们c语言结构体的一种特殊用法,并没有什么新奇之处。我们先来看一下变长结构体的一种通用定义方法。
typedef struct bucket {
    int n;
    char key[30];
    char value[1];
} bucket;
我们定义了一个结构体bucket,我们希望用这个结构体存放学生的个人简介。其中key用来存在学生的姓名,value用来存放学生的简介。大家可能很好奇,我们的value声明了长度为1. 1个char能存多少信息呀?
         其实,对于变长结构体,我们在使用的使用不能直接定义变量,例如:bucket bucket; 您要是这样使用,value肯定存储不了多少信息。对于变长结构体,我们在使用的时候需要先声明一个变长结构体的指针,然后通过malloc函数分配函数空间,我们需要用到的空间长度是多少,我们就可以malloc多少。通用的使用方法如下:
bucket* pbucket;
pbucket = malloc(sizeof(bucket) + n * sizeof(char));
其中n就是你要使用value的长度。如果这样使用的话,value指向的字符串不久变长了吗!
1.2     hashtable简介
我们先看一下hashtable的定义
struct _hashtable;
typedef struct bucket {
    ulong h;//当元素是数字索引时使用
    uint nkeylength;//当使用字符串索引时,这个变量表示索引的长度,索引(字符串)保存在最后一个元素akey
    void *pdata;//用来指向保存的数据,如果保存的数据是指针的话,pdataptr就指向这个数据,pdata指向pdataptr
    void *pdataptr;
    struct bucket *plistnext; //上一个元素
    struct bucket *plistlast; //下一个元素
    struct bucket *pnext; //指向下一个bucket的指针
    struct bucket *plast; //指向上一个bucket的指针
    char arkey[1]; //必须放在最后,主要是为了实现变长结构体
} bucket;
typedef struct _hashtable {
    uint ntablesize;             //哈希表的大小
    uint ntablemask;             //数值上等于ntablesize- 1
    uint nnumofelements;         //记录了当前hashtable中保存的记录数
    ulong nnextfreeelement;      //指向下一个空闲的bucket
    bucket *pinternalpointer;    //这个变量用于数组反转
    bucket *plisthead;            //指向bucket的头
    bucket *plisttail;            //指向bucket的尾
    bucket **arbuckets;
    dtor_func_t pdestructor;     //函数指针,数组增删改查时自动调用,用于某些清理操作
    zend_bool persistent;         //是否持久
    unsigned char napplycount;
    zend_bool bapplyprotection;  //和napplycount一起起作用,防止数组遍历时无限递归
#if zend_debug
    int inconsistent;
#endif
} hashtable;
希望大家能好好看看上面的定义,有些东西我将出来反而会说不明白,不如大家看看代码浅显明了。php的数组,其实是一个带有头结点的双向链表,其中hashtable是头,bucket存储具体的结点信息。
1.3     hashtable内部函数分析
1.3.1    宏hash_protect_recursion
#definehash_protect_recursion(ht)                                                     \
    if ((ht)->bapplyprotection) {                                                       \
        if ((ht)->napplycount++ >= 3){                                                \
            zend_error(e_error, nestinglevel too deep - recursive dependency?); \
        }                                                                               \
    }
这个宏主要用来防止循环引用。
1.3.2    宏zend_hash_if_full_do_resize
#definezend_hash_if_full_do_resize(ht)            \
    if ((ht)->nnumofelements >(ht)->ntablesize) {  \
        zend_hash_do_resize(ht);                    \
    }
         这个宏的作用是检查目前hashtable中的元素个数是否大于了总的hashtable的大小,如果个数大于了hashtable的大小,那么我们就重新分配空间。我们看一下zend_hash_do_resize
static int zend_hash_do_resize(hashtable *ht)
{
    bucket **t;
    is_consistent(ht);
    if ((ht->ntablesize 0) {   /* let's double the table size */
        t = (bucket**) perealloc_recoverable(ht->arbuckets,
(ht->ntablesize persistent);
        if (t) {
            handle_block_interruptions();
            ht->arbuckets = t;
            ht->ntablesize = (ht->ntablesize             ht->ntablemask = ht->ntablesize - 1;
            zend_hash_rehash(ht);
            handle_unblock_interruptions();
            return success;
        }
        return failure;
    }
    return success;
}  
         从上面的代码中我们可以看出,hashtable在分配空间的时候,新分配的空间等于原有空间的2倍。
1.3.3    函数 _zend_hash_init
这个函数是用来初始化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; //hashtable的大小默认无2的3次方
    bucket **tmp;
set_inconsistent(ht_ok);
if (nsize >= 0x80000000) {
        ht->ntablesize = 0x80000000;
    } else {
        while ((1u             i++;
        }
        ht->ntablesize = 1     }
ht->ntablemask = ht->ntablesize- 1;
    ht->pdestructor = pdestructor;
    ht->arbuckets = null;
    ht->plisthead = null;
    ht->plisttail = null;
    ht->nnumofelements = 0;
    ht->nnextfreeelement = 0;
    ht->pinternalpointer = null;
    ht->persistent = persistent;
    ht->napplycount = 0;
    ht->bapplyprotection = 1;
/* uses ecalloc() so that bucket* == null */
    if (persistent) {
        tmp = (bucket **) calloc(ht->ntablesize, sizeof(bucket*));
        if (!tmp) {
            return failure;
        }
        ht->arbuckets = tmp;
    } else {
        tmp = (bucket **) ecalloc_rel(ht->ntablesize, sizeof(bucket*));
        if (tmp) {
            ht->arbuckets = tmp;
        }
    }
return success;
}
可以看出,hashtable的大小被初始化为2的n次方,另外我们看到有两种内存方式,一种是calloc,一种是ecalloc_rel,这两中内存分配方式我们细讲了,有兴趣的话大家可以自己查一查。
1.3.4    函数_zend_hash_add_or_update
这个函数向hashtable中添加或者修改元素信息
zend_api int _zend_hash_add_or_update(hashtable *ht, const char *arkey, uint nkeylength, void *pdata, uint ndatasize, void **pdest, int flag zend_file_line_dc)
{
    ulong h;
    uint nindex;
    bucket *p;
is_consistent(ht);
if (nkeylength #if zend_debug
        zend_puts(zend_hash_update: can't put inempty key\n);
#endif
        return failure;
    }
h = zend_inline_hash_func(arkey, nkeylength);
    nindex = h & ht->ntablemask;
p = ht->arbuckets[nindex];
    while (p != null) {
        if ((p->h == h) && (p->nkeylength == nkeylength)) {
            if (!memcmp(p->arkey, arkey, nkeylength)) {
                if (flag & hash_add) {
                    return failure;
                }
                handle_block_interruptions();
#if zend_debug
                if (p->pdata == pdata) {
                    zend_puts(fatal error in zend_hash_update:p->pdata == pdata\n);
                    handle_unblock_interruptions();
                    return failure;
                }
#endif
                if (ht->pdestructor) {
                    ht->pdestructor(p->pdata);
                }
                update_data(ht, p, pdata, ndatasize);
                if (pdest) {
                    *pdest = p->pdata;
                }
                handle_unblock_interruptions();
                return success;
            }
        }
        p = p->pnext;
    }
p = (bucket *) pemalloc(sizeof(bucket) - 1 + nkeylength, ht->persistent);
    if (!p) {
        return failure;
    }
    memcpy(p->arkey, arkey, nkeylength);
    p->nkeylength = nkeylength;
    init_data(ht, p, pdata, ndatasize);
    p->h = h;
    connect_to_bucket_dllist(p, ht->arbuckets[nindex]);
    if (pdest) {
        *pdest = p->pdata;
    }
handle_block_interruptions();
    connect_to_global_dllist(p, ht);
    ht->arbuckets[nindex] = p;
    handle_unblock_interruptions();
ht->nnumofelements++;
    zend_hash_if_full_do_resize(ht);        /* if the hash table is full, resize it */
    return success;
}
1.3.5    宏connect_to_bucket_dllist
#define connect_to_bucket_dllist(element, list_head)        \
    (element)->pnext= (list_head);                         \
    (element)->plast= null;                                \
    if((element)->pnext) {                                 \
        (element)->pnext->plast =(element);                \
    }
这个宏是将bucket加入到bucket链表中
1.3.6    其他函数或者宏定义
我们只是简单的介绍一下hashtable,如果你想细致的了解hashtable的话,建议您看看php的源代码,关于hashtable的代码在zend/zend_hash.h 和zend/zend_hash.c中。
zend_hash_add_empty_element 给函数增加一个空元素
zend_hash_del_key_or_index 根据索引删除元素
zend_hash_reverse_apply  反向遍历hashtable
zend_hash_copy  拷贝
_zend_hash_merge  合并
zend_hash_find  字符串索引方式查找
zend_hash_index_find  数值索引方法查找
zend_hash_quick_find  上面两个函数的封装
zend_hash_exists  是否存在索引
zend_hash_index_exists  是否存在索引
zend_hash_quick_exists  上面两个方法的封装
1.4     c扩展常用hashtable函数
虽然hashtable看起来有点复杂,但是使用却是很方便的,我们可以用下面的函数对hashtable进行初始化和赋值。
2005 年地方院校招生人数
php语法
c语法
意义
$arr = array()
array_init(arr);
初始化数组
$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(3.14);
$arr[] = ‘foo’;
add_next_index_string(arr, “foo”, 1);
1的意思进行字符串拷贝
$arr[] = $myvar;
add_next_index_zval(arr, myvar);
$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] = $myvar;
add_index_zval(arr, 5, myvar);
$arr[“abc”] = null;
add_assoc_null(arr, “abc”);
$arr[“def”] = 711;
add_assoc_long(arr, “def”, 711);
$arr[“ghi”] = true;
add_assoc_bool(arr, ghi”, 1);
$arr[“jkl”] = 1.44;
add_assoc_double(arr, “jkl”, 1.44);
$arr[“mno”] = ‘baz’;
add_assoc_string(arr, “mno”, “baz”, 1);
$arr[‘pqr’] = $myvar;
add_assoc_zval(arr, “pqr”, myvar);
1.5     任务和实验
说了这么多,我们实验一下。
任务:返回一个数组,数组中的数据如下:
array
(
   [0] => for test
   [42] => 123
   [for test. for test.] => 1
   [array] => array
       (
           [0] => 3.34
       )
)
代码实现:
php_function(test)
{
    zval* t;
array_init(return_value);
    add_next_index_string(return_value, for test, 1);
    add_index_long(return_value, 42, 123);
    add_assoc_double(return_value, for test. for test., 1.0);
alloc_init_zval(t);
    array_init(t);
    add_next_index_double(t, 3.34);
add_assoc_zval(return_value, array, t);
}
很简单吧,还记得return_value吗?
http://www.bkjia.com/phpjc/477779.htmlwww.bkjia.comtruehttp://www.bkjia.com/phpjc/477779.htmltecharticle1 数组 本节我们讲一下php的数组,在php中,数组使用hashtable实现的。本节中我们先详细的介绍一下hashtable,然后再讲讲如何使用hasttable 1.1...
其它类似信息

推荐信息