你真的懂i++和++i嗎?從字節碼理解它吧!

i++

      在初次學習i++++i時我們就一直牢記着:i++是先使用後加1,++i是先加1再使用,長時間都一直這樣搬着磚(這結論當然是沒毛病的),我也是。直到我看見了下面的題目。

public static void func(){
    int i=0;
    for(int j=0;j < 100;j++){
        i = i++;
    }
    Stystem.out.println(i);//打印什麼???
}

      我是覺得打印100的,毫無疑問,我錯了。 對了也就沒此文了。
      看來我還是太菜了,看看字節碼吧!

 0 iconst_0
 1 istore_0
 2 iconst_0
 3 istore_1
 4 iload_1
 5 bipush 100
 7 if_icmpge 21 (+14)

10 iload_0
11 iinc 0,1
14 istore_0

15 iinc 1,1
18 goto 4 (-14)
21 getstatic #2 <java/lang/System.out>
24 iload_0
25 invokevirtual #3 <java/io/PrintStream.println>
28 return

      其中10~14對應的就是i=i++的字節碼。強烈建議給IDEA安裝上Jclasslib插件,這樣能更方便的指定助記符的含義。

  • iload_0: 將當前棧幀的局部變量數組的索引爲0的局部變量的值推送到操作數堆棧上。
  • iinc 0,1: 將索引爲0的局部變量按1遞增。
  • istore_0: 將值從操作數堆棧中彈出(記爲val),並且將索引爲0的局部變量值設置爲val。

      看完這三個助記符我們就會發現iinc雖然講值遞增了,但是沒有設置給i,所以i的值一直沒有變,i++完全沒有任何作用。動圖如下:

字節碼操作流程

      嗯,就這樣,i被改變後又被棧上的值給覆蓋了_

++i

      現在我們換一下題,換成++i

public static void func(){
    int i=0;
    for(int j=0;j < 100;j++){
        i = ++i;
    }
    Stystem.out.println(i);//打印什麼???
}

      對應字節碼如下。

 0 iconst_0
 1 istore_0
 2 iconst_0
 3 istore_1
 4 iload_1
 5 bipush 100
 7 if_icmpge 21 (+14)

10 iinc 0,1
13 iload_0
14 istore_0

15 iinc 1,1
18 goto 4 (-14)
21 getstatic #2 <java/lang/System.out>
24 iload_0
25 invokevirtual #3 <java/io/PrintStream.println>

      依舊是不變的10~14行,
請輸入圖片描述

      如此i就可以被改變了,打印100。

混合操作

      在i++++i的筆試題中經常混合使用,一堆+號頭疼。如:i= (i++)+(i++)+(++i)+(++i);。咱先來個簡單的(如i = i++ + ++i;),否則字節碼都迷糊了。其實這個的答案很簡單,只是我們需要通過它的字節碼更理解而已。字節碼如下。

 0 iconst_0
 1 istore_0

 2 iload_0
 3 iinc 0,1
 6 iinc 0,1
 9 iload_0
10 iadd
11 istore_0

12 getstatic #2 <java/lang/System.out>
15 iload_0
16 invokevirtual #3 <java/io/PrintStream.println>
19 return

      我們只需要關注2~11行,這幾步對應的值和棧如下圖所示。畫圖這也是本文的目的所在。先來學習一個助記符。

iadd
Both value1 and value2 must be of type int. The values are popped from the operand stack. The int result is value1 + value2. The result is pushed onto the operand stack.
 
The result is the 32 low-order bits of the true mathematical result in a sufficiently wide two’s complement format, represented as a value of type int. If overflow occurs, then the sign of the result may not be the same as the sign of the mathematical sum of the two values.
 
Despite the fact that overflow may occur, execution of an iadd instruction never throws a run-time exception.
 
嗯,iadd在加完後會將值在壓入棧中,切記!!

請輸入圖片描述

困難模式

      來,畫它i= (i++)+(i++)+(++i)+(++i);,看看看官能否畫出過程圖呢_。字節碼如下。

 0 iconst_0
 1 istore_0

 2 iload_0
 3 iinc 0,1
 6 iload_0
 7 iinc 0,1
10 iadd
11 iinc 0,1
14 iload_0
15 iadd
16 iinc 0,1
19 iload_0
20 iadd
21 istore_0

22 getstatic #2 <java/lang/System.out>
25 iload_0
26 invokevirtual #3 <java/io/PrintStream.println>
29 return

      我們只需要關注2~21行。

請輸入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章