加载中…
个人资料
郭新华
郭新华
  • 博客等级:
  • 博客积分:0
  • 博客访问:14,597
  • 关注人气:25
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
正文 字体大小:

php数组复制的实现

(2015-01-15 12:53:23)
标签:

it

php源码

在php内部复制数组我们经常会这么写:

zval *conf = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &conf) == FAILURE) {
   return;
}
zval tmp;
tmp = *conf;
zval_copy_ctor(&tmp);


这段逻辑很简单,接受一个用户传进来的数组参数,然后将其复制一份给tmp这个变量。
我们注意这句:tmp = *conf;  这其实就是c语言的结构体变量赋值和下面这段是一样的意思:
struct  xinhua
{
int   a;
int   b;
}A,B;
A=B;
记得上学的时候谭浩强老人家说这样的结构体变量赋值是不行的,坑!而php手册里面zval复制介绍这块用大量的篇幅描述为“=”号的运算符重载,对于新学者还是会有蒙圈的感觉,会认为这个tmp=*conf;是运算符重载,个人觉得php手册描述的不妥。

赋值完毕后,tmp这个zval结构所有成员的值都和conf一样了包括ht这个指针的值。
btw:我们脑中一定有这样一个概念,那就是php里面所有的变量都是用zval表示,而数组在php内部是通过hashtable来表示的,zval通过ht这个指针指向这个hashtable。
现在我们通过gdb打断点来查看tmp和conf的ht指针。
php数组复制的实现
我们看到ht指针的值是相同的,这验证了我们上面的说法,但是这显然有悖于我们数组复制的目的,现在该zval_copy_ctor出场了!

zval_copy_ctor最终会调用_zval_copy_ctor_func这个zend API我们直接看他的源码:
          case IS_ARRAY:
       case IS_CONSTANT_ARRAY: {
               zval *tmp;
               HashTable *original_ht = zvalue->value.ht;
               HashTable *tmp_ht = NULL;
               TSRMLS_FETCH();

               if (zvalue->value.ht == &EG(symbol_table)) {
                   return;
               }
               ALLOC_HASHTABLE_REL(tmp_ht);
               zend_hash_init(tmp_ht, zend_hash_num_elements(original_ht), NULL, ZVAL_PTR_DTOR, 0);
               zend_hash_copy(tmp_ht, original_ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
               zvalue->value.ht = tmp_ht;
           }
           break;


我们看到php通过ALLOC_HASHTABLE_REL(tmp_ht);重新在堆上分配了一个hashtable结构体,同时调用zend_hash_init初始化hash表,紧接着调用zend_hash_copy复制conf的hashtable,最后把复制好的hashtable地址赋值给我们最终的tmp的ht指针。
我们通过gdb查看下:

php数组复制的实现
此时这tmp和conf这两个zval所引用的hashtable已经不是一个了。

我们再注意下zend_hash_copy函数的(copy_ctor_func_tzval_add_ref这块,其实zend_hash_copy的内部实现就是遍历hashtable,然后给每个元素都执行我们传进来的zval_add_ref这个函数。

也就是说真正数组的元素并没有复制,而是添加引用计数这样的方式来避免性能损耗。

over ~

0

阅读 评论 收藏 转载 喜欢 打印举报/Report
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 电话:4000520066 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有