### 7.7.6 数组操作 #### 7.7.6.1 创建数组 创建一个新的HashTable分为两步:首先是分配zend_array内存,这个可以通过`ZVAL_NEW_ARR()`宏分配,也可以自己直接分配;然后初始化数组,通过`zend_hash_init()`宏完成,如果不进行初始化数组将无法使用。 ```c #define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent) \ _zend_hash_init((ht), (nSize), (pDestructor), (persistent) ZEND_FILE_LINE_CC) ``` * __ht:__ 数组地址HashTable*,如果内部使用可以直接通过emalloc分配 * __nSize:__ 初始化大小,只是参考值,这个值会被对齐到2^n,最小为8 * __pHashFunction:__ 无用,设置为NULL即可 * __pDestructor:__ 删除或更新数组元素时会调用这个函数对操作的元素进行处理,比如将一个字符串插入数组,字符串的refcount增加,删除时不是简单的将元素的Bucket删除就可以了,还需要对其refcount进行处理,这个函数就是进行清理工作的 * __persistent:__ 是否持久化 示例: ```c zval array; uint32_t size; ZVAL_NEW_ARR(&array); zend_hash_init(Z_ARRVAL(array), size, NULL, ZVAL_PTR_DTOR, 0); ``` #### 7.7.6.2 插入、更新元素 数组元素的插入、更新主要有三种情况:key为zend_string、key为普通字符串、key为数值索引,相关的宏及函数: ```c // 1) key为zend_string //插入或更新元素,会增加key的refcount #define zend_hash_update(ht, key, pData) \ _zend_hash_update(ht, key, pData ZEND_FILE_LINE_CC) //插入或更新元素,当Bucket类型为indirect时,将pData更新至indirect的值,而不是更新Bucket #define zend_hash_update_ind(ht, key, pData) \ _zend_hash_update_ind(ht, key, pData ZEND_FILE_LINE_CC) //添加元素,与zend_hash_update()类似,不同的地方在于如果元素已经存在则不会更新 #define zend_hash_add(ht, key, pData) \ _zend_hash_add(ht, key, pData ZEND_FILE_LINE_CC) //直接插入元素,不管key存在与否,如果存在也不覆盖原来元素,而是当做哈希冲突处理,所有会出现一个数组中key相同的情况,慎用!!! #define zend_hash_add_new(ht, key, pData) \ _zend_hash_add_new(ht, key, pData ZEND_FILE_LINE_CC) // 2) key为普通字符串:char* //与上面几个对应,这里的key为普通字符串,会自动生成zend_string的key #define zend_hash_str_update(ht, key, len, pData) \ _zend_hash_str_update(ht, key, len, pData ZEND_FILE_LINE_CC) #define zend_hash_str_update_ind(ht, key, len, pData) \ _zend_hash_str_update_ind(ht, key, len, pData ZEND_FILE_LINE_CC) #define zend_hash_str_add(ht, key, len, pData) \ _zend_hash_str_add(ht, key, len, pData ZEND_FILE_LINE_CC) #define zend_hash_str_add_new(ht, key, len, pData) \ _zend_hash_str_add_new(ht, key, len, pData ZEND_FILE_LINE_CC) // 3) key为数值索引 //插入元素,h为数值 #define zend_hash_index_add(ht, h, pData) \ _zend_hash_index_add(ht, h, pData ZEND_FILE_LINE_CC) //与zend_hash_add_new()类似 #define zend_hash_index_add_new(ht, h, pData) \ _zend_hash_index_add_new(ht, h, pData ZEND_FILE_LINE_CC) //更新第h个元素 #define zend_hash_index_update(ht, h, pData) \ _zend_hash_index_update(ht, h, pData ZEND_FILE_LINE_CC) //使用自动索引值 #define zend_hash_next_index_insert(ht, pData) \ _zend_hash_next_index_insert(ht, pData ZEND_FILE_LINE_CC) #define zend_hash_next_index_insert_new(ht, pData) \ _zend_hash_next_index_insert_new(ht, pData ZEND_FILE_LINE_CC) ``` #### 7.7.6.3 查找元素 ```c //根据zend_string key查找数组元素 ZEND_API zval* ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key); //根据普通字符串key查找元素 ZEND_API zval* ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *key, size_t len); //获取数值索引元素 ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h); //判断元素是否存在 ZEND_API zend_bool ZEND_FASTCALL zend_hash_exists(const HashTable *ht, zend_string *key); ZEND_API zend_bool ZEND_FASTCALL zend_hash_str_exists(const HashTable *ht, const char *str, size_t len); ZEND_API zend_bool ZEND_FASTCALL zend_hash_index_exists(const HashTable *ht, zend_ulong h); //获取数组元素数 #define zend_hash_num_elements(ht) \ (ht)->nNumOfElements //与zend_hash_num_elements()类似,会有一些特殊处理 ZEND_API uint32_t zend_array_count(HashTable *ht); ``` #### 7.7.6.4 删除元素 ```c //删除key ZEND_API int ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key); //与zend_hash_del()类似,不同地方是如果元素类型为indirect则同时销毁indirect的值 ZEND_API int ZEND_FASTCALL zend_hash_del_ind(HashTable *ht, zend_string *key); ZEND_API int ZEND_FASTCALL zend_hash_str_del(HashTable *ht, const char *key, size_t len); ZEND_API int ZEND_FASTCALL zend_hash_str_del_ind(HashTable *ht, const char *key, size_t len); ZEND_API int ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong h); ZEND_API void ZEND_FASTCALL zend_hash_del_bucket(HashTable *ht, Bucket *p); ``` #### 7.7.6.5 遍历 数组遍历类似foreach的用法,在扩展中可以通过如下的方式遍历: ```c zval *val; ZEND_HASH_FOREACH_VAL(ht, val) { ... } ZEND_HASH_FOREACH_END(); ``` 遍历过程中会把数组元素赋值给val,除了上面这个宏还有很多其他用于遍历的宏,这里列几个比较常用的: ```c //遍历获取所有的数值索引 #define ZEND_HASH_FOREACH_NUM_KEY(ht, _h) \ ZEND_HASH_FOREACH(ht, 0); \ _h = _p->h; //遍历获取所有的key #define ZEND_HASH_FOREACH_STR_KEY(ht, _key) \ ZEND_HASH_FOREACH(ht, 0); \ _key = _p->key; //上面两个的聚合 #define ZEND_HASH_FOREACH_KEY(ht, _h, _key) \ ZEND_HASH_FOREACH(ht, 0); \ _h = _p->h; \ _key = _p->key; //遍历获取数值索引key及value #define ZEND_HASH_FOREACH_NUM_KEY_VAL(ht, _h, _val) \ ZEND_HASH_FOREACH(ht, 0); \ _h = _p->h; \ _val = _z; //遍历获取key及value #define ZEND_HASH_FOREACH_STR_KEY_VAL(ht, _key, _val) \ ZEND_HASH_FOREACH(ht, 0); \ _key = _p->key; \ _val = _z; #define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val) \ ZEND_HASH_FOREACH(ht, 0); \ _h = _p->h; \ _key = _p->key; \ _val = _z; ``` #### 7.7.6.6 其它操作 ```c //合并两个数组,将source合并到target,overwrite为元素冲突时是否覆盖 #define zend_hash_merge(target, source, pCopyConstructor, overwrite) \ _zend_hash_merge(target, source, pCopyConstructor, overwrite ZEND_FILE_LINE_CC) //导出数组 ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source); ``` ```c #define zend_hash_sort(ht, compare_func, renumber) \ zend_hash_sort_ex(ht, zend_sort, compare_func, renumber) ``` 数组排序,compare_func为typedef int (*compare_func_t)(const void *, const void *),需要自己定义比较函数,参数类型为Bucket*,renumber表示是否更改键值,如果为1则会在排序后重新生成各元素的h。PHP中的sort()、rsort()、ksort()等都是基于这个函数实现的。 #### 7.7.6.7 销毁数组 ```c ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht); ```