php算法题实用,php算法题,请路过的高人指点下

问题1 执行结果为:1

问题2 执行结果为:bool(true)

原理解释如下:

关键词:PHP 运算符的优先级 递增/递减运算符规则

演示版本:PHP-5.6

原理分析角度:PHP源码/++操作符源码

PHP 运算符的优先级

bVXVDr?w=1674&h=1214

如上所示, || 优先级 高于 = ,所以 || 返回的结果 true (boolean 类型) 复制给了变量左操作数(如题中的变量 $a)。

而赋值表达式的返回结果为左操作数的值,所以这两处可以满足 if 条件。

参考:运算符优先级

++ 运算符的计算规则

递增/递减运算符不影响布尔值。递减 NULL 值也没有效果,但是递增 NULL 的结果是 1。(参考:递增/递减运算符

从 PHP 手册 上我们可以看到 ++ 运算符对 boolean 类型变量无效,所以 问题1 中的 echo 结果为 1 , var_dump 结果为 bool(true) 。

至此,我们已经明白了这两段代码片段有这样结果的原因了。

PHP 源码角度分析 ++ 操作符实现逻辑

(以 问题1 为例)

1. 利用 vld 查看 此段代码的 opcode

➜ answer git:(master) ✗ cat -n question1.php

1 <?php

2

3 $a = 1;

4 $b = 1;

5

6 if ($a = 1 || $b = 1) {

7 ++$a;

8 }

9

10 echo $a;

11 var_dump($a);

➜ answer git:(master) ✗ php -dvld.active=1 -dvld.verbosity=3 question1.php

opcode 结果如下:

bVXVEg?w=2268&h=1098

定位第七行代码,我们可以看到 ++ 操作:

opcode 为 PRE_INC

操作数类型 为 IS_CV(篮筐内容)

2. 从 PHP 源码 角度分析 ++ 操作符

2.1 词法分析

bVXVED?w=828&h=270

文件:Zend/zend_language_scanner.l +1343

如图示:++ 操作符所对应的 Token 为:T_INC

2.2 语法分析

bVXVFp?w=2442&h=244

文件:Zend/zend_language_parser.y +795

如图示:通过 T_INC 我们在语法解析文件中定位到了 ++ 操作符所对应的语法解析规则。

我们可以得到:

语法处理函数:zend_do_pre_incdec

opcode : ZEND_PRE_INC

分析 zend_do_pre_incdec 方法,及根据 opcode 所对应的 zend_vm 处理方法,我们可以知道,最终 opcode 在 zend_vm 中执行时,所调用的方法是 ZEND_PRE_INC_SPEC_CV_HANDLER (此处根据 opcode (ZEND_PRE_INC) 和 ++ 操作数类型 IS_CV 判断)。

查看 ZEND_PRE_INC_SPEC_CV_HANDLER 方法,源码如下:

static int ZEND_FASTCALL ZEND_PRE_INC_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)

{

USE_OPLINE

zval **var_ptr;

SAVE_OPLINE();

var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_RW(execute_data, opline->op1.var TSRMLS_CC);

if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) {

zend_error_noreturn(E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets");

}

if (IS_CV == IS_VAR && UNEXPECTED(*var_ptr == &EG(error_zval))) {

if (RETURN_VALUE_USED(opline)) {

PZVAL_LOCK(&EG(uninitialized_zval));

EX_T(opline->result.var).var.ptr = &EG(uninitialized_zval);

}

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);

EX_T(opline->result.var).var.ptr = *var_ptr;

}

CHECK_EXCEPTION();

ZEND_VM_NEXT_OPCODE();

}

我们可以看到,此次调用最终会执行 fast_increment_function 方法。

继续跟进 fast_increment_function 方法

bVXVHn?w=1328&h=1590

我们会发现调用了 increment_function,此方法源码如下:

ZEND_API int increment_function(zval *op1) /* {{{ */

{

switch (Z_TYPE_P(op1)) {

case IS_LONG:

if (Z_LVAL_P(op1) == LONG_MAX) {

/* switch to double */

double d = (double)Z_LVAL_P(op1);

ZVAL_DOUBLE(op1, d+1);

} else {

Z_LVAL_P(op1)++;

}

break;

case IS_DOUBLE:

Z_DVAL_P(op1) = Z_DVAL_P(op1) + 1;

break;

case IS_NULL:

ZVAL_LONG(op1, 1);

break;

case IS_STRING: {

long lval;

double dval;

switch (is_numeric_string(Z_STRVAL_P(op1), Z_STRLEN_P(op1), &lval, &dval, 0)) {

case IS_LONG:

str_efree(Z_STRVAL_P(op1));

if (lval == LONG_MAX) {

/* switch to double */

double d = (double)lval;

ZVAL_DOUBLE(op1, d+1);

} else {

ZVAL_LONG(op1, lval+1);

}

break;

case IS_DOUBLE:

str_efree(Z_STRVAL_P(op1));

ZVAL_DOUBLE(op1, dval+1);

break;

default:

/* Perl style string increment */

increment_string(op1);

break;

}

}

break;

case IS_OBJECT:

if (Z_OBJ_HANDLER_P(op1, do_operation)) {

zval *op2;

int res;

TSRMLS_FETCH();

MAKE_STD_ZVAL(op2);

ZVAL_LONG(op2, 1);

res = Z_OBJ_HANDLER_P(op1, do_operation)(ZEND_ADD, op1, op1, op2 TSRMLS_CC);

zval_ptr_dtor(&op2);

return res;

}

return FAILURE;

default:

return FAILURE;

}

return SUCCESS;

}

我们传入的操作数类型(boolean)不满足所有的 case,所以逻辑走到了 default 而最终执行了 return FAILURE;,再根据函数调用栈返回,我们发现没有对操作数做任何递增处理。

结论

此时,我们可以得出最开始的结论:

递增运算符不影响布尔值。

(当然 递减 操作符分析同理)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值