7.7.6 数组操作
最后更新于:2022-04-02 05:19:01
### 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);
```
';