• 从字节码的角度理解i++、++i和i++ + ++i


    本文主要分析i++、++i和i++ + ++i的结果,这些也是面试题中最常考的,但有的读者可能总是记不住,原因就在于读者未能深入到字节码层面做进一步的研究,本文将从字节码的角度探索此类语法的奥秘。

    本文演示案例源码:

    int a = 10;
    a = a++;
    
    int b = 10;
    b = ++b;
    
    
    System.out.println(a);
    System.out.println(b);
    
    
    int d = 10;
    int e = d++ + ++d;
    System.out.println(e);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    通过javap -c命令,将类的字节码文件反汇编得到如下内容:

    Code:
           0: bipush        10
           2: istore_1
           3: iload_1
           4: iinc          1, 1
           7: istore_1
           8: bipush        10
          10: istore_2
          11: iinc          2, 1
          14: iload_2
          15: istore_2
          16: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
          19: iload_1
          20: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
          23: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
          26: iload_2
          27: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
          30: bipush        10
          32: istore_3
          33: iload_3
          34: iinc          3, 1
          37: iinc          3, 1
          40: iload_3
          41: iadd
          42: istore        4
          44: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
          47: iload         4
          49: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
          52: return
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    与本例相关的字节码指令:

    指令作用
    bipush将单字节的常量值推送至栈顶
    istore_1将栈顶int类型数值存入第2个本地变量,同时栈顶的值要出栈
    iload_1将第2个int类型本地变量推送至栈顶
    iinc将指定int类型变量增加指定值(如i++,i--、i+=2)
    getstatic获取指定类的静态字段,并将其值压入栈顶
    invokevirtual调用实例方法
    iadd将栈最前面的两int类型数值相加并将结果压入栈顶
    return从当前方法返回void

    先来分析a++,对应汇编代码的第0到第7行。

    0: bipush        10   # 将常量10压入栈顶
    2: istore_1           # 将栈顶的10赋值给第2个本地变量,即a=10,同时10出栈
    3: iload_1            # 将a的值10再次压入栈顶
    4: iinc          1, 1 # 第一个1表示是第2个本地变量,第二个1就是数值1,意思是将数值1加到第2个本地变量上,即a=a+1=10+1=11
    7: istore_1           # 将栈顶的值赋给第2个本地变量,由于栈顶的值还是10,因此相当于a=10
    
    • 1
    • 2
    • 3
    • 4
    • 5

    汇编代码第16到第20行,其实就是打印a,根据上面的分析可知,此时打印的a的值是10。

    再来分析++b,对应汇编代码的第8到第15行。

    8: bipush        10    # 将常量10压入栈顶
    10: istore_2           # 将栈顶的10赋值给第3个本地变量,即b=10,同时10出栈
    11: iinc          2, 1 # 2表示第3个本地变量,1就是数值1,意思是将数值1加到第3个本地变量上,即b=b+1=10+1=11
    14: iload_2            # 将第3个本地变量的值放到栈顶,此时栈顶的值是11
    15: istore_2           # 将栈顶的值存入第3个本地变量,即b=11
    
    • 1
    • 2
    • 3
    • 4
    • 5

    汇编代码第23到第27行,其实就是打印b,根据上面的分析可知,此时打印的b的值是11。

    根据上面对a++和++b的分析,总结如下:

    • 相同点:最后会将栈顶的值赋给a或b
    • 不同点:++b会将b+1的结果马上置入栈顶,而a++则不会,这也就直接导致了两者最终结果的不同

    最后再来分析i++ + ++i,对应汇编代码的第30到第42行。

    30: bipush        10   # 将常量10压入栈顶
    32: istore_3           # 将栈顶的值存入第4个本地变量,即d=10,同时10出栈
    33: iload_3            # 将第4个本地变量的值压入栈顶
    34: iinc          3, 1 # 将数值1加到第4个本地变量上,即d=d+1=10+1=11
    37: iinc          3, 1 # 将数值1加到第4个本地变量上,即d=d+1=11+1=12
    40: iload_3            # 将第4个本地变量的值压入栈顶,此时栈顶的值为12,而随后就是10
    41: iadd               # 将栈的最前面两个int值相加,即10+12=22,并将结果压入栈顶
    42: istore        4    # 将栈顶的值存入第5个本地变量,即e=22
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    汇编代码第44到第49行,其实就是打印e,根据上面的分析可知,此时打印的e的值是22。

    我们一开始在学习C语言的时候就接触到了i++和++i的区别,前者是先用后加,而后者则是先加后用,这是的先用后用说的就是将加1的结果压入栈顶的时机。

    对于i++ + ++i来说,假设i=10,那么从前往后分析,i++即将i的值加1,i此时为11,但i++这个表达式的值是10,对于++i,也是将i的值加1,但此时i的值已经是11了,再加1的话是12,且整个表达式的值就是12,那么最终结果就是10+12=22。

  • 相关阅读:
    vscode 阅读 linux kernel 源码
    Vue插件
    PX4模块设计之二十:PX4应用平台初始化
    Docker部署RustDesk Server 设置开机自启
    创建超小的Golang docker 镜像
    替换Nginx并且自动申请免费的HTTPS证书?
    重学设计模式之代理模式一 静态代理
    查看电脑的 CPU 信息
    layer弹出框详细说明
    Python面向对象(二)
  • 原文地址:https://blog.csdn.net/xl_1803/article/details/126438545