前几天折腾了一个php的扩展, 发现这个扩展里面的c代码也是把php里面的数组作为抽象数据结构来使用,写下此文记录下在c环境下php数组的使用
类型
php的数组其实也是用zval来定义的,所以说php的数组要用一个zval指针来保存
zval*output;
先初始化 zval 指针, 这里用了 ALLOC_INIT_ZVAL 这个宏
ALLOC_INIT_ZVAL(output);
这样就完成了zval的初始化
初始化
zval初始化完成之后这个zval还不是一个数组类型的zval,所以要做数组的初始化,用array_init函数
array_init(output);
调用完array_init之后不出意外这个zval对象就被指定为Array类型了
添加索引
有了Array对象之后我们就可以像php里面一样设置索引了,我在网上找了一些参考的函数
//add_assoc_*系列函数,生成字符索引:
add_assoc_null(zval *aval,char*key);
add_assoc_bool(zval *aval,char*key, zend_bool bval);
add_assoc_long(zval *aval,char*key,longlval);
add_assoc_double(zval *aval,char*key,doubledval);
add_assoc_string(zval *aval,char*key,char*strval,intdup);
add_assoc_stringl(zval *aval,char*key,char*strval,uintstrlen,intdup);
add_assoc_zval(zval *aval,char*key, zval *value);
//备注:其实这些函数都是宏,都是对add_assoc_*_ex函数的封装。
//add_index_*系列函数,生成整型索引:
ZEND_APIintadd_index_long (zval *arg,ulongidx,longn);
ZEND_APIintadd_index_null (zval *arg,ulongidx );
ZEND_APIintadd_index_bool (zval *arg,ulongidx,intb );
ZEND_APIintadd_index_resource (zval *arg,ulongidx,intr );
ZEND_APIintadd_index_double (zval *arg,ulongidx,doubled);
ZEND_APIintadd_index_string (zval *arg,ulongidx,constchar*str,intduplicate);
ZEND_APIintadd_index_stringl (zval *arg,ulongidx,constchar*str,uintlength,intduplicate);
ZEND_APIintadd_index_zval (zval *arg,ulongindex, zval *value);
//add_next_index_long函数,生成连续的整型索引:
ZEND_APIintadd_next_index_long (zval *arg,longn );
ZEND_APIintadd_next_index_null (zval *arg );
ZEND_APIintadd_next_index_bool (zval *arg,intb );
ZEND_APIintadd_next_index_resource (zval *arg,intr );
ZEND_APIintadd_next_index_double (zval *arg,doubled);
ZEND_APIintadd_next_index_string (zval *arg,constchar*str,intduplicate);
ZEND_APIintadd_next_index_stringl (zval *arg,constchar*str,uintlength,intduplicate);
ZEND_APIintadd_next_index_zval (zval *arg, zval *value);
哈希表
从上面的参考函数可以发现我们给数组添加元素的时候都是使用zval指针来作为操作的对象的,按道理来说更新和删除也是这样的,不过zend并没有提供使用zval指针来更新和删除数组的函数,必须先获得一个HashTable指针,估计是php Array 底层也是用HashTable实现的吧,获得HashTable指针的方法:
HashTable*ht=Z_ARRVAL_P(output);
用Z_ARRVAL_P这个宏就可以了
删除和更新
从上文中知道要删除和更新Array里面的内容要先获得HashTable的指针,获得之后就可以用一些函数来删除和更新数据了,我也从网上找了一些参考的函数
我们可以通过zend_hash_num_elements()这个宏来获取一个哈希表的元素个数,当然我们也可以通过它们来得到一个数组变量中的数组的个数。
对于数组中元素的删除,我们这里介绍三个比较常用的API吧,它们是:
第一个,我们通过zend_hash_del()函数来删除数组中的字符串下标对应的元素,它的第一个参数为HashTable类型的哈希表,它的第二个参数为zend_string类型的字符串。
第二个,我们通过zend_hash_str_del()函数来删除数组中的字符串下标对应的元素,它的第一个参数为HashTable类型的哈希表,它的第二个参数是字符串,第三个参数是字符串的长度。
第三个,我们通过zend_hash_index_del()函数来删除数组中的整型值下标对应的元素,它的第一个参数为HashTable类型的哈希表,它的第二个参数为zend_ulong类型的非负整型值。
这两个函数的常见的函数原型如下所示:ZEND_API int ZEND_FASTCALL zend_hash_del(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_index_del(HashTable *ht, zend_ulong h);
对于修改数组中的元素,Zend引擎同样给我们提供了丰富的API,它们都是用宏函数的方式提供的,这里我们介绍常见的三个吧:
第一个即zend_hash_index_update(ht, h, pData),它用来修改整数型下标的数据。
第二个即zend_hash_str_update(ht, key, len, pData),它通过字符串及其长度的方式来修改字符串下标的数据。
第三个即zend_hash_update(ht, key, pData),它通过传递一个zend_string的指针的方式来修改字符串下标的数据。
需要说明的是,在上述的宏函数中,pData都是zval*的类型。而且上面介绍的三个宏函数都是如果哈希表中没有相应的元素会执行添加,如果有相应的元素则执行修改,跟我们平时操作PHP数组的习惯是一致的。
对象释放
我们知道ALLOC_INIT_ZVAL会创建一个zval对象并返回一个指针,如果在c函数内用完了就不释放肯定是会造内存泄露的,
可以用宏FREE_ALLOC_ZVAL 来释放zval对象的内存
FREE_ALLOC_ZVAL(output)
这样这个对象就被释放了,不过有的时候情况比较复杂,比如在c里面创建了zval对象然后传递给php,虽然c里面是不用这个对象了,
但是php那边可能还是在使用这个对象的,所以可以操作该对象的引用计数,让php引擎自动回收,
zval_ptr_dtor函数可以把zval对象的引用计数减去1
zval_ptr_dtor(&output)