两道PHP体算法题,不懂,请路过的大神帮忙阐述一下原理
问题1 执行结果为:1问题2 执行结果为:bool(true)
1
bool(true)
原理解释如下:关键词:PHP 运算符的优先级 递增/递减运算符规则演示版本:PHP-5.6原理分析角度:PHP源码/++操作符源码
PHP 运算符的优先级
递增/递减运算符规则
PHP-5.6
PHP源码
++操作符源码
如上所示, || 优先级 高于 = ,所以 || 返回的结果 true (boolean 类型) 复制给了变量左操作数(如题中的变量 $a)。而赋值表达式的返回结果为左操作数的值,所以这两处可以满足 if 条件。参考:运算符优先级
||
=
true
boolean
$a
if
递增/递减运算符不影响布尔值。递减 NULL 值也没有效果,但是递增 NULL 的结果是 1。(参考:递增/递减运算符
从 PHP 手册 上我们可以看到 ++ 运算符对 boolean 类型变量无效,所以 问题1 中的 echo 结果为 1 , var_dump 结果为 bool(true) 。
PHP 手册
++
问题1
echo
var_dump
至此,我们已经明白了这两段代码片段有这样结果的原因了。
(以 问题1 为例)
➜ 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 结果如下:定位第七行代码,我们可以看到 ++ 操作:
opcode
PRE_INC
操作数类型
IS_CV
参考:PHP-5.6 源码
文件:Zend/zend_language_scanner.l +1343如图示:++ 操作符所对应的 Token 为:T_INC
Zend/zend_language_scanner.l +1343
Token
T_INC
文件:Zend/zend_language_parser.y +795如图示:通过 T_INC 我们在语法解析文件中定位到了 ++ 操作符所对应的语法解析规则。我们可以得到:
Zend/zend_language_parser.y +795
zend_do_pre_incdec
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_vm
ZEND_PRE_INC_SPEC_CV_HANDLER
查看 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 方法我们会发现调用了 increment_function,此方法源码如下:
fast_increment_function
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;,再根据函数调用栈返回,我们发现没有对操作数做任何递增处理。
case
default
return FAILURE;
此时,我们可以得出最开始的结论:
递增运算符不影响布尔值。
(当然 递减 操作符分析同理)
递减
上下两张图片都是赋值=操作,赋值操作的结果为true,所以每次都能进入到if中,完毕
$a = 1 || $b = 1 可以看成 $a = (1 || $b = 1),$a = true;对PHP不熟,但是如果你自己实现过四则运算,这问题其实很简单。:)
近似代码
$a = 1; if ($a = true) { ++$a; // ++/–-运算符不影响布尔值 }
if中使用"="号的赋值操作看等号左侧值最终的值来进行判断
if($a = $b) { // ... }
若$b值为false、null、''、[]、0等值时,以上无法进入block代码块你也可以把上面的代码理解为如下
$a = $b; if($a) { // ... }
这道题还能难倒人的,我是出题者会在判断里面赋予变量b其他值然后让答题者回答变量b的值
= 等运算优先级 比|| 低, 所以得到 $a=true
这个不叫算法题叫大家来找茬输出2
写这代码的是不是写错了?这个判断根本没有意义。两张图里的if中的参数等于全是赋值为1了,不进行比较。直接输出if中的值,第一张图输出为2,第二张图输出为bool(true)
$a = 1,$b = 1
脑筋急转弯?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
暂无简介
文章 0 评论 0
接受
发布评论
评论(11)
问题1 执行结果为:
1
问题2 执行结果为:
bool(true)
原理解释如下:
关键词:
PHP 运算符的优先级
递增/递减运算符规则
演示版本:
PHP-5.6
原理分析角度:
PHP源码
/++操作符源码
PHP 运算符的优先级
如上所示,
||
优先级 高于=
,所以||
返回的结果true
(boolean
类型) 复制给了变量左操作数(如题中的变量$a
)。而赋值表达式的返回结果为左操作数的值,所以这两处可以满足
if
条件。参考:运算符优先级
++ 运算符的计算规则
从
PHP 手册
上我们可以看到++
运算符对boolean
类型变量无效,所以问题1
中的echo
结果为1
,var_dump
结果为bool(true)
。至此,我们已经明白了这两段代码片段有这样结果的原因了。
PHP 源码角度分析 ++ 操作符实现逻辑
(以
问题1
为例)1. 利用 vld 查看 此段代码的 opcode
opcode
结果如下:定位第七行代码,我们可以看到
++
操作:opcode
为PRE_INC
操作数类型
为IS_CV
(篮筐内容)2. 从 PHP 源码 角度分析 ++ 操作符
参考:PHP-5.6 源码
2.1 词法分析
文件:
Zend/zend_language_scanner.l +1343
如图示:
++
操作符所对应的Token
为:T_INC
2.2 语法分析
文件:
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
方法,源码如下:我们可以看到,此次调用最终会执行
fast_increment_function
方法。继续跟进
fast_increment_function
方法我们会发现调用了
increment_function
,此方法源码如下:我们传入的操作数类型(
boolean
)不满足所有的case
,所以逻辑走到了default
而最终执行了return FAILURE;
,再根据函数调用栈返回,我们发现没有对操作数做任何递增处理。结论
此时,我们可以得出最开始的结论:
(当然
递减
操作符分析同理)上下两张图片都是赋值
=
操作,赋值操作的结果为true
,所以每次都能进入到if中,完毕$a = 1 || $b = 1 可以看成 $a = (1 || $b = 1),$a = true;
对PHP不熟,但是如果你自己实现过四则运算,这问题其实很简单。:)
近似代码
if中使用"="号的赋值操作
看等号左侧值最终的值来进行判断
若$b值为false、null、''、[]、0等值时,以上无法进入block代码块
你也可以把上面的代码理解为如下
这道题还能难倒人的,我是出题者会在判断里面赋予变量b其他值然后让答题者回答变量b的值
= 等运算优先级 比|| 低, 所以得到 $a=true
这个不叫算法题
叫大家来找茬
输出2
写这代码的是不是写错了?这个判断根本没有意义。
两张图里的if中的参数等于全是赋值为1了,不进行比较。直接输出if中的值,第一张图输出为2,第二张图输出为bool(true)
$a = 1,$b = 1
脑筋急转弯?