• 理解PHP的变量,值与引用的关系


    ---
    title: 理解PHP的变量,值与引用的关系
    createdDate: 2015-03-11
    category: php
    ---

    PHP的变量与C++中的变量是两种截然不容的概念。如果没有理解清楚,使用C++的方式来思考PHP就会遇到一些问题。

    C++中,变量与值是绑定的。值是内存的上的一块内存上的数据,而变量则是操作这块内存的名称。变量消失(比如超出作用域)值也会消失。

    而PHP中,变量和值是两个概念。PHP是一种弱类型语言,值在PHP的内部(zend引擎),被存放在一个zval结构体中,这个结构体中,除了包含了值的类型,数据外,还包含两个字节的信息。一个是`is_ref`,是一个bool值,用来标识这个值是否是一个`引用`。第二个额外字节是`refcount`,用来表示指向这个值的变量(也称符号即symbol)的个数。如果refcount为0,那么这个值就可以被回收了。

    而变量则另外存放在一个符号表中,每个变量有其作用域。

    声明一个变量时:

    ```
    $a = 'hello';
    ```

    在当前作用域新建了一个变量a,并在内存中新建了一个zval,其类型字段为string,数据字段为'hello'。因为a不是一个引用变量,所以is_ref字段为false,因为$a指向了这个zval(注意,虽然是指向这个zval了,但是a依然不是引用变量),所以refcount为1。关系大概类似这样:



    注意一点,**当"refcount"的值是1时,"is_ref"的值总是FALSE**。这一点往后就明白了。

    ## 输出"refcount"和"is_ref"

    如果按照了xdebug,可以使用`xdebug_debug_zval()`函数来输出"refcount"和"is_ref"的值。

    ```
    $a = 'hello';
    xdebug_debug_zval('a');
    ```

    输出:
    ```
    (refcount=1, is_ref=0),string 'hello' (length=5)
    ```

    我们可以利用这个函数来更好的理解zval。

    ## 赋值的小细节

    先来看那PHP中赋值的例子:

    ```
    $a = "hello";
    $b = $a;
    xdebug_debug_zval( 'a' );
    xdebug_debug_zval( 'b' );
    ```

    输出

    ```
    a:
    (refcount=2, is_ref=0),string 'hello' (length=5)
    b:
    (refcount=2, is_ref=0),string 'hello' (length=5)
    ```

    既然PHP中除了object的赋值外都是复制,那么为什么a的refcount会增加呢?

    我们修改b的值看看会不会有什么变化:

    ```
    $a = "hello";
    $b = $a;
    xdebug_debug_zval( 'a' );
    xdebug_debug_zval( 'b' );

    $b = 2;
    xdebug_debug_zval('a');
    xdebug_debug_zval('b');
    ```

    输出:
    ```
    a:
    (refcount=2, is_ref=0),string 'hello' (length=5)
    b:
    (refcount=2, is_ref=0),string 'hello' (length=5)
    a:
    (refcount=1, is_ref=0),string 'hello' (length=5)
    b:
    (refcount=1, is_ref=0),int 2
    ```

    看到了么!如果只是赋值而没有修改的话,PHP不会立马拷贝值,而是让b指向a的值,所以a的zval的refcount为2。但是当我们修改b时,PHP就必须进行复制了。所以a的zval的refcount回到1。

    用图片表示更直观:

    $b = $a:



    修改b后:



    ## 理解引用

    理解了PHP中值与引用的关系,再来看PHP的引用就非常简单了。引用变量不会有新开一个zval,而是指向他所引用的变量的zval。并且,zval的`is_ref`置为1,refcount加一。

    把上面的例子改为引用赋值,看看区别:
    ```
    a:
    (refcount=2, is_ref=1),string 'hello' (length=5)
    b:
    (refcount=2, is_ref=1),string 'hello' (length=5)
    a:
    (refcount=2, is_ref=1),int 2
    b:
    (refcount=2, is_ref=1),int 2
    ```

    输出:

    ```
    a:
    (refcount=2, is_ref=1),string 'hello' (length=5)
    b:
    (refcount=2, is_ref=1),string 'hello' (length=5)
    a:
    (refcount=2, is_ref=1),int 2
    b:
    (refcount=2, is_ref=1),int 2
    ```

    可以看出a和b都是指向一个zval的变量。



    在代码的最后把a变量删除会有什么效果呢?

    ```
    $a = "hello";
    $b = &$a;
    xdebug_debug_zval( 'a' );
    xdebug_debug_zval( 'b' );

    $b = 2;
    xdebug_debug_zval('a');
    xdebug_debug_zval('b');

    unset($a);
    xdebug_debug_zval('b');
    ```

    输出:
    ```
    a:
    (refcount=2, is_ref=1),string 'hello' (length=5)
    b:
    (refcount=2, is_ref=1),string 'hello' (length=5)
    a:
    (refcount=2, is_ref=1),int 2
    b:
    (refcount=2, is_ref=1),int 2
    b:
    (refcount=1, is_ref=0),int 2
    ```

    删除a变量后,b的zval的refcount减为1,这个可以理解,这里需要注意的是,b作为一个引用变量,现在他的zval的`is_ref`为0!也就是它不再是引用变量了。

    这是因为a变量被删除后,b变为唯一一个指向这个zval的变量了,也因此,他也就摆脱了引用变量这个身份。这也就是上文所说的**当"refcount"的值是1时,"is_ref"的值总是FALSE**



    ## 参考网址
    - PHP: 引用计数基本知识 - Manual




  • 相关阅读:
    Vue 多环境的配置 look
    01 java基本类型和包装类型的区别? look
    03 java自动装箱与拆箱了解吗?原理是什么? look
    Windows下MySQL的安装和删除 look
    02 java包装类型的缓存机制 look
    test
    keepalived 主备搭建及配置
    rename批量重命名文件名
    keepalived执行stop命令无法退出进程问题
    职场PUA
  • 原文地址:https://www.cnblogs.com/mushan/p/4330386.html
Copyright © 2020-2023  润新知