你真的懂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行。

请输入图片描述

原创文章 235 获赞 1297 访问量 23万+

猜你喜欢

转载自blog.csdn.net/qq_25343557/article/details/106153082