gpt4 book ai didi

php - 递增时的字符串连接

转载 作者:IT王子 更新时间:2023-10-29 00:58:00 25 4
gpt4 key购买 nike

这是我的代码:

$a = 5;
$b = &$a;
echo ++$a.$b++;

它不应该打印 66 吗?

为什么它打印 76?

最佳答案

好吧。这实际上是非常直接的行为,它与引用在 PHP 中的工作方式有关。这不是错误,而是意外行为。

PHP 在内部使用写时复制。这意味着在您写入内部变量时会复制内部变量(因此 $a = $b; 在您实际更改其中一个之前不会复制内存)。对于引用,它永远不会真正复制。这对以后很重要。

让我们看看这些操作码:

line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
2 0 > ASSIGN !0, 5
3 1 ASSIGN_REF !1, !0
4 2 PRE_INC $2 !0
3 POST_INC ~3 !1
4 CONCAT ~4 $2, ~3
5 ECHO ~4
6 > RETURN 1

前两个应该很容易理解。
  • 分配 - 基本上,我们正在分配 5 的值进入名为 !0 的编译变量.
  • ASSIGN_REF - 我们正在创建来自 !0 的引用至 !1 (方向无所谓)

  • 到目前为止,这是直截了当的。现在是有趣的一点:
  • PRE_INC - 这是实际增加变量的操作码。值得注意的是,它将结果返回到一个名为 $2 的临时变量中。 .

  • 那么让我们看看 the source code behind PRE_INC 当用变量调用时:
    static int ZEND_FASTCALL  ZEND_PRE_INC_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
    {
    USE_OPLINE
    zend_free_op free_op1;
    zval **var_ptr;

    SAVE_OPLINE();
    var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);

    if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
    zend_error_noreturn(E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets");
    }
    if (IS_VAR == IS_VAR && UNEXPECTED(*var_ptr == &EG(error_zval))) {
    if (RETURN_VALUE_USED(opline)) {
    PZVAL_LOCK(&EG(uninitialized_zval));
    AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval));
    }
    if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
    CHECK_EXCEPTION();
    ZEND_VM_NEXT_OPCODE();
    }

    SEPARATE_ZVAL_IF_NOT_REF(var_ptr);

    if (UNEXPECTED(Z_TYPE_PP(var_ptr) == IS_OBJECT)
    && Z_OBJ_HANDLER_PP(var_ptr, get)
    && Z_OBJ_HANDLER_PP(var_ptr, set)) {
    /* proxy object */
    zval *val = Z_OBJ_HANDLER_PP(var_ptr, get)(*var_ptr TSRMLS_CC);
    Z_ADDREF_P(val);
    fast_increment_function(val);
    Z_OBJ_HANDLER_PP(var_ptr, set)(var_ptr, val TSRMLS_CC);
    zval_ptr_dtor(&val);
    } else {
    fast_increment_function(*var_ptr);
    }

    if (RETURN_VALUE_USED(opline)) {
    PZVAL_LOCK(*var_ptr);
    AI_SET_PTR(&EX_T(opline->result.var), *var_ptr);
    }

    if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
    CHECK_EXCEPTION();
    ZEND_VM_NEXT_OPCODE();
    }

    现在我不希望您立即理解它在做什么(这是深度引擎巫术),但让我们来看看它。

    前两个 if 语句检查变量是否“安全”递增(第一个检查它是否是一个重载对象,第二个检查该变量是否是特殊错误变量 $php_error)。

    接下来是我们真正有趣的部分。由于我们正在修改该值,因此它需要执行写时复制。所以它调用:
    SEPARATE_ZVAL_IF_NOT_REF(var_ptr);

    现在,请记住,我们已经将变量设置为上面的引用。所以变量没有被分离......这意味着我们在这里对它所做的一切都会发生在 $b上。还有……

    接下来,变量递增( fast_increment_function() )。

    最后,它设置结果 本身 .这又是写时复制。它不会返回 操作,但实际 变量 .那又怎样 PRE_INC返回是 仍然是对 $a 的引用和 $b .
  • POST_INC - 这与 PRE_INC 的行为类似,除了一个非常重要的事实。

  • 让我们看看 the source code再次:
    static int ZEND_FASTCALL  ZEND_POST_INC_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
    {
    retval = &EX_T(opline->result.var).tmp_var;
    ZVAL_COPY_VALUE(retval, *var_ptr);
    zendi_zval_copy_ctor(*retval);

    SEPARATE_ZVAL_IF_NOT_REF(var_ptr);
    fast_increment_function(*var_ptr);
    }

    这次我删掉了所有不有趣的东西。那么让我们看看它在做什么。

    首先,它获取返回临时变量(上面代码中的 ~3)。

    那么它 副本将其参数( !1$b )中的值转换为结果(因此引用被破坏)。

    然后它增加参数。

    现在记住,论点 !1是变量 $b ,其中引用了 !0 ( $a ) $2 ,如果你还记得的话,这是来自 PRE_INC 的结果.

    所以你有它。它返回 76,因为该引用保留在 PRE_INC 的结果中。

    我们可以通过强制复制来证明这一点,首先将 pre-inc 分配给一个临时变量(通过正常分配,这会破坏引用):
    $a = 5;
    $b = &$a;
    $c = ++$a;
    $d = $b++;
    echo $c.$d;

    这按您的预期工作。 Proof

    我们可以通过引入一个函数来维护引用来重现其他行为(你的错误):
    function &pre_inc(&$a) {
    return ++$a;
    }

    $a = 5;
    $b = &$a;
    $c = &pre_inc($a);
    $d = $b++;
    echo $c.$d;

    正如你所看到的那样有效(76): Proof

    注意:这里单独函数的唯一原因是 PHP 的解析器不喜欢 $c = &++$a; .所以我们需要通过函数调用添加一个间接级别来做到这一点......

    我不认为这是一个错误的原因是它是引用应该如何工作的。预先递增引用的变量将返回该变量。即使是未引用的变量也应该返回该变量。这可能不是您在这里所期望的,但它几乎在所有其他情况下都可以很好地工作......

    基础点

    如果您正在使用引用,那么您在 99% 的时间里都做错了。所以不要使用引用,除非你 绝对需要他们。 PHP 在内存优化方面比您想象的要聪明得多。而且您对引用的使用确实阻碍了它的工作方式。因此,虽然您认为自己可能正在编写智能代码,但实际上在绝大多数情况下,您编写的代码效率和友好度都较低……

    如果您想了解更多关于引用以及变量在 PHP 中的工作原理,请查看 One Of My YouTube Videos就此主题而言...

    关于php - 递增时的字符串连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17188279/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com