你真的理解$i++吗

136 阅读2分钟
你真的理解$i++
在php课程中讲到
$i++
++$i
的时候,通常并不会讲底层实现的原理,导致学生稍微碰到一些
”出乎意料”
的题目就会糊涂,我曾在学生中间做过调查,发现当问起
$i=2;$i=$i++;echo $i;
结果是多少的时候
,
大家众说纷纭再也没有了一致口径,而且几乎没有人能回答正确,偶尔碰到正确答案,细问之下也会发现,他们并不肯定,原因就是他们并不理解这个
$i++
底层实现的原理。
于是我去网上找资料,发现也是人云亦云,理解的偏于肤浅,并不准确,我认为一定是我们的教科书出现了问题,才会导致普遍性的错误,于是翻看各类程序语言的书籍,终于发现了在全国最为流行的
C
语言教材中《
C
程序设计
(
第四版
)
(
谭浩强 著
)
的第
53
页,看到了这样一句代码和解释:
j=i++;(
先将
i
的值
3
赋给
j
j
的值为
3
,然后
i
变为
4)
。问题就出在这里!很多人都按照这个理解顺理成章地认为先给别的变量赋值然后自己再自增!结果是很多人解释各类编程语言的
i++
时也都是这样解释的,才导致普遍性的理解错误。
我们还回到
PHP
,在
PHP
中的
$i++
C
语言中的
i++
其实并无本质区别,所以很多老师照搬了
C
语言教科书中的解释,但是按照这个解释,我们改造一下谭老师的例题:
$i=2;$i=$i++;echo $i;
按照大多数人的理解
(
就是按照教科书中的理解
)
,先将原来的
$i
的值赋给
$i
,然后
$i
再自增
1
,所以在后一行输出的时候应该是
3
才对,但是结果为什么是
2
呢?我们该到底怎么理解
$i++?
我们可以将
$i++
看成一个表达式,这个表达式是有值的,他的值就是没有自增之前的
$i
的值,所以我们直接输出
$i++
这个表达式的值或者把
$i++
作为一个整体赋值给其他变量的时候,其他变量得到的都是没有自增之前的
$i
;
这个表达式同时还会使
$i
自增,可以看成是一个带加法运算的数学表达式
(
这样说仅是帮助理解
)
,那么这么表达式执行的时候是怎样的呢?对于
$j=$i++
的执行过程,可以这样理解:
$temp=$i;//
先用一个临时变量将
$i
原来的值保存起来
$i=$i+1;//
完成自增
1
$j=$temp;//
将原来保存的值赋给
$j
这个变量
从这个来看,可以得到
2
点结论:
1.
赋值是在最后完成的而不是大家通常认为的
“先赋值再自增”
;
2.
$i++
给其他变量赋值或者直接输出
$i++
时得到的是“值”,而不是变量,这一点与
++$i
不同,
++$i
返回的是变量
$i
。后面会有例子证明这一点。
明白了上面的执行过程,再来看为什么
$i=2;$i=$i++;echo $i
最终输出的是
2
就很简单了:
先将
2
临时保存起来
;
自己自增
1
变成
3;
再把先前保存起来的
2
赋值给自己,这样
$i
中间变成了
3
但最后又变成了
2;
如果再深究,还会牵涉到内存和寄存器,但我觉得已经没必要了,只要能理解他的执行过程就够了。
至于
++$i
,大家可以按照老师讲的理解就行了。
你是不是豁然开朗了?跃跃欲试了?那我给你出几道题练练手吧。
① $i=2; $j= $i+$i + $i++ * ++$i; echo $j; //
结果是多少
?
② $i=2; $j= $i + $i++ * ++$i; echo $j; //
结果是多少
?


③ $i=2; $j=(++$i)+(++$i); echo $j; //
结果是多少?
④ $i=2; $q=&$i;$j=(++$i)+(++$i);echo $j; //
结果是多少?


⑤ $i=2; $j=($i++)+($i++); echo $j; //
结果是多少?
⑥ $i=2; $q=&$i; $j=($i++)+($i++); echo $j; //
结果是多少?
嘿,有结果了吗?是不是又犯晕了?估计又是错了一大片。上面这些题目,①②是一组,③④是一组,⑤⑥是一组,同时④⑥也很相似。完全无障碍解决上面的题目,你可能需要懂一点编译原理,懂一点变量引用的知识。陈独秀请坐下,下面是我的装逼时刻,我要开始我的表演了!我先公布答案:
① 12;② 12;③ 7;④ 8;⑤ 5;⑥ 5;
我们先来看下:
①和②明显不一样,但是结果为什么一样?
③④与⑤⑥形式一样,但是③④结果不同而⑤⑥结果却一样,为什么?
权威解析如下:
关于
①和②,如果你在学编译原理这门课时写过四则混合运算器,就非常用以理解了。什么?你是非专业?那也没关系!我有把握把你教会!
当运算符两边的变量并不能直接运算的时候,计算机不是直接把变量的值取出来放在变量的位置上,而是把这个变量压入栈中,直到运算符两端的变量能参加运算了,才取出变量对应的值进行运算。举个例子:
a + b*c;a和b不能直接相加,因为后面的运算优先级更高,所以程序不是把a变量现在对应的值取出来放在a的位置上,而是把a变量放入栈中,直到b*c有了结果,比如结果是250,此时运算变成了a + 250,两边均可以参加运算了,再取出a变量目前对应的值进行计算。按照这个理解,$j= $i+$i + $i++ * ++$i;
执行过程如下:
$j=2+2+$i++*++$i=4+$i++*++$i=4+2*4=12;
其中
$i++
执行完之后
$i
变为
3
,故
++$i
4
;但是
$j= $i+ $i++ * ++$i
少加了一个
$i,
类似于
a + b*c
结构,显然乘法的优先级更高,故加号前面的
$i
并不是取出现在的值放在他的位置上,而是把这个变量压入栈中,当后面的
$i++ * ++$i=2*4=8
有了结果时
(
此时
$i
已经变成了
4)
,计算式变成了
$j=$i+8;
此时两边可以直接计算了,再取出此时
$i
的值
4
进行相加,自然就是
$j=4+8=12
。惊不惊喜?意不意外?可是一切又合情合理。
关于③④与⑤⑥,是不是也出乎你的意料?嘿嘿,一切意外其实皆有道理,请听我的下回分解。