Java變數自增表示式 i = i++ 的底層邏輯(簡述)
前言
很多老師告訴我們,i = i++ 的運算過程是 temp = i; i ++; i = temp; 所以i的值不變。但我總覺得這個temp的出現有些莫名其妙。所以在網上檢索之後,把大佬們的解釋做了一點總結和簡化,權當拾人牙慧。
要搞懂 i = i++ 我們先要簡單認識兩個東西:區域性變數表 和 運算元棧。
- 運算元棧:一個臨時的儲存空間, 主要用於儲存計算過程的變數和中間結果,
- 區域性變數表:也是一個臨時的儲存空間,它用於儲存函式的引數以及區域性變數。
- 對於本文章而言,初學者可以把它看做兩個桶:一個用來裝i的值(區域性變數表),另一個用來裝中間過程中用到的數(運算元棧)
正文
瞭解了以上內容,就可以對i=i++進行理解了。不過在此之前,我們先看看單獨的i++ 和 ++i的區別:
i++ 和 ++i
i++ 和 ++i,它們實際上是直接在區域性變數表裡修改變數的值,原地修改,不需要經過運算元棧。所以,作為語句單獨使用時,沒有區別。
public static void main(String[] args) {
int i = 0;
i++; // 在區域性變數表直接自增
++i; // 在區域性變數表直接自增,沒有區別
}
那麼當情況是 i = i++時; 為什麼結果就是i的值不變呢?
i = i++
public static void main(String[] args) {
int i = 0;
i = i++; // i值不變
}
這就是運算元棧參與的結果,上面程式碼的執行過程,實際上是這樣的:
- 執行 int i = 0;
- 把0這個常數放到運算元棧中
- 從運算元棧頂取出常數0,然後儲存到區域性變數表的索引為1的位置(區域性變數表[1]),這個位置就代表i的值(因為區域性變數表裡有args 和 i 兩個元素,args的索引是0)。
- 執行 i = i++;
- 計算機首先看見右側表示式中的i, 所以它把區域性變數表[1]的值取出,壓入運算元棧。
- 計算機又看見了符號“++”,於是把區域性變數表[1]進行自增
- 然後計算機看見 “=” ,所以對等號左邊的i進行賦值,重點來了:
- 這裡賦的是哪個值呢? -- 運算元棧裡的值,0。
- 那麼賦值到哪裡呢? -- 區域性變數表[1]。直接覆蓋了自增的結果,也就是說,剛剛的自增操作,增了個寂寞。
- 所以我們就知道了:由於剛剛是“先壓棧,再自增”,所以棧裡的值還是原始值,最後又覆蓋回去了。
- 同理,我們也就知道它和 i = ++i 的不同之處在哪裡了
- 計算機這次首先看見的是“++”符號,而不是i, 所以它這次先把區域性變數表[1]進行自增
- 然後計算機才看見了i,此時才把區域性變數表[1]的值取出,壓入運算元棧,因此,棧裡的值也變成了1,最後覆蓋回去就是1。
以上順序的變化,實際上在JVM編譯後的位元組碼檔案中能夠直觀地看到,但是初學者對位元組碼很陌生,所以採用了以上的描述方式。位元組碼的區別其實更加直觀,如下:
- i = i++ 的執行順序:
iconst_0 # 把int常量0壓入運算元棧
istore_1 # 把運算元棧頂的值"0"儲存到區域性變數表[1]
iload_1 # 區域性變數表[1]的值壓入運算元棧頂,此時運算元棧頂為0
iinc 1 1 # 將區域性變數表[1]的值,加上1, 所以此時i的值變成1
istore_1 # 將運算元棧頂的值儲存到區域性變數表[1], 用0覆蓋了1.
- i = ++i 的執行順序:
iconst_0 # 同上
istore_1 # 同上
iinc 1 1 # 將區域性變數表[1]的值,加上1, 此時i的值變成1
iload_1 # 區域性變數表[1]的值壓入運算元棧頂,此時運算元棧頂為1
istore_1 # 將運算元棧頂的值儲存到區域性變數表[1], 用1覆蓋了1.
這個問題的進階還有 k = i + ++i * i++ 參考文章:https://blog.csdn.net/See_Star/article/details/125206538
作者: 練塊兒的程式設計師
出處:https://www.cnblogs.com/sunyujun16
本文版權歸作者和部落格園共有,歡迎轉載,但必須保留此段宣告,且在文章頁面明顯位置給出原文連結, 如有問題,可郵件sunyujun16@163.com諮詢.