PHP 一個詭異的加法算法的研究

前言

以前我在面試的時候,遇到許多年輕人都聲稱本身精通php,有過許多項目經驗等等。然而,當真正筆試的時候,我問到php

$result=1;
if(-1){
 $result=2;
}
echo $result;

中,$result最終結果的時候,許多人信誓旦旦的告訴我是1。 試想,這樣一個連基本算法都搞不清楚的人,即便有過再多的項目經驗,你敢用嗎?面試

引伸

對於算法的一些問題,我我的一貫是很是較真的,我招人的時候也是很是側重此方面,所以我對php的關注也是在這方面多些。故事得從一個知乎上的問題開始。算法

//第一題
<?php
function test(){
 $a=1;
 $b=&$a;
 echo (++$a)+(++$a);
}
test();
//執行的值爲6
?>
//第二題
<?php
function test(){
 $a=1;
 $b=&$a;
 echo (++$a)+(++$a)+(++$a);
}
test();
//執行的值爲10
?>

這個問題很是的有意思,也是一個大坑,許多人都算錯了,包括不少我認識的大牛。在這裏就不點名字,以避免其羞愧。 然而你覺得我一開始算對了嗎?我算對了第一題,第二題倒是百思不得其解。最後用調試工具一番調試纔算理清頭緒。索性發出來與你們一塊兒分享這個有意思的問題。php7

分析

第一題

php語言解釋

這個其實很是簡單,++a這種單目運算符的運算結果仍是自身。 因此工具

$a=1;
$b=&$a;
echo (++$a)+(++$a);
//換種寫法就等同於
$a=1;
$a=++$a; //2
$a=++$a; //3
$a=$a+$a;//3+3=6

哈,不少人確定覺得是等於5,然而這個是操做的同一個變量,等同於改變了兩次$a的值,最後相加的時候,天然就是改變後的值相加,因此等於6。spa

正常狀況

然而在php中,爲了照顧人類的邏輯,默認狀況下,即便名字相同的基本類型的變量,也不會使用同一個變量地址,所以,以上代碼會被解析爲debug

$a=1;
echo (++$a)+(++$a);
//換種寫法就等同於
$a=1;
$a=++$a; //2
$b=++$a; //3
$a=$a+$b;//2+3=5

可是因爲調試

$b=&$a;

的存在,使得下面的第一個$a的計算方式變成了傳統的c語言計算方式,因此輸出了_看起來錯誤的結果_。
然而實際上,這個結果反而是正確的。PHP中的糖語法寵壞了那些基礎原本不紮實的孩子,對這種加法作了特別的運算處理而已。code

深刻思考

爲何說等於6纔是正確結果呢?咱們知道現行高級語言大多來自c語言,php也不例外,咱們這裏用c語言來寫一遍上述代碼,而後經過反彙編來看看機器究竟是怎麼執行的。 blog

alt

其實不管是否註釋下面的取地址,結果都是6。 咱們看彙編代碼

alt

這裏更清晰的看到a的值的變化。 便是

mov edx,dword ptr [a] ;a的值爲3
add edx,dword ptr [a] ; 3+3

問題二

問題發現

由問題一的結論來分析問題二,反而陷入了一個更大的舞曲,爲何呢?

echo (++$a)+(++$a)+(++$a);  //10
/*
按照問題一的分析,此處的結果應爲
a=1+1  //2
a=a+1  //3
a=a+1  //4
a=a+a+a //12
*/

然而輸出的結果倒是10.

動手實驗

這裏咱們用一個工具phpdebug.exe來調試下看看。

alt

能夠看到的a的值的變化:

1->2->3->4

而後咱們再來調試一下注釋掉

$b=&$a;

的結果。

alt

這裏在第二步的時候從新給了變量$a一個地址,實際上同是叫$a,其實他們已經不是同一個變量了。 因此他輸出的結果爲9。

實驗結果

可是爲何上面的結果爲10呢? 這實際上是由於

$b=&$a;

這個取地址運算只起效了一句運算指令,就是隻管事了第一回合,對於之後的運算,php仍是用了日常的算法。 即:

$a=1;
$b=&$a;
echo (++$a)+(++$a)+(++$a);
/*
這段其實是
$a=++$a; //2
$a=++$a; //3
//注意了,前兩個已經獲得結果了,第三個咱們用一個新的變量$c。
$c=++$a; //4
$a=$a+$a; //3+3=6
$a=$a+$c; //6+4=10
*/

結論

我認爲出現這種詭異的結果應該算是php的bug,同時,這也說明了此種問題不太容易被發現和暴露,這要求咱們日常寫代碼的時候儘可能使用經常使用的語法,和精幹的語句,讓代碼和邏輯達到最佳的平衡點。 此BUG我已經反饋到php官方。

後續

最新的php7中已經修復了此bug。

修訂記錄

  • 初稿 2015-09-11

  • 修訂 2016-05-27

  • 修訂 2016-05-30

相關文章
相關標籤/搜索