当前位置:编程学习 > php >>

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

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 << 1) > 0) {   /* Let's double the table size */
        t = (Bucket**) perealloc_recoverable(ht->arBuckets,
(ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent);
        if (t) {
            HANDLE_BLOCK_INTERRUPTIONS();
            ht->arBuckets = t;
            ht->nTableSize = (ht->nTableSize << 1);
            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) < nSize) {
            i++;
        }
        ht->nTableSize = 1 << i;
    }
 
    ht->nTableMask = ht->nTableSize- 1;
    ht->pDestructor = pDestructor;
补充:Web开发 , php ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,