首页 > 其他分享 >关于 i=i++ 的字节码原理

关于 i=i++ 的字节码原理

时间:2022-11-09 01:45:13浏览次数:37  
标签:10 操作数 字节 ++ 局部变量 自增 原理 变量

在学 jvm 之前,我也认为 i++ 与 ++i 的区别就是 一个先使用 i 的值,再自增;一个先自增,再使用 i 的值。直到我遇到了 i = i++ 。

 
int i = 10;
i = i++;
System.out.println(i);

 

按照正常逻辑,先把 i 的值赋给 i 本身,此时 i 为10;然后再执行自增,最后的结果应该是 11 。

不出意外的话就该出意外了,当我输入这段代码,按下执行的时候,得到的 i 的值却是 10,而并非 11。

 

我也有问过一些老师,得到的答案是:i++ 就等同于 i=i+1;然后再用 i 去接收,就等同于 i=i=i++,这种写法是错误的,而且实际开发也不可能这样写。

显然,这并不能说服我。

 

网上有一种说法是:

JVM 在处理 i=i++ 时,会建立一个临时变量来接收 i++ 的值 , 然后返回这个临时变量的值 , 返回的值再被等号左边的变量接收了 , 这样就是说 i 虽然自增了但是又被赋为原来的值 , 这样输出的结果自然就是 10 了。

也就等同如下代码:

int i = 0;
int temp = i;
i++;
i=temp System.out.println(i);

 

这样虽然可以解释清楚为什么 i=i++ 不会改变 i 的值,但是对于追求底层原理的人来说,显然还是有存在被质疑的点,就只是跟你说会用一个零食变量保存 i 的值,似乎还是没有告诉你到底为什么会这样。

 

然后,只能去看编译之后的字节码指令。

 

这里需要先了解:

  1. java虚拟机中,一个方法被调用时,会在虚拟机栈里压入一个栈帧,方法执行完之后,栈帧弹出;
  2. 每一个栈帧又包括 局部变量表、操作数栈 等一些其他的东西;
  3. 局部变量表用于保存方法用到的局部变量,操作数栈用于对变量的一些操作;

 

知道了这些,就可以看字节码指令了。

通过  javac -v 文件名  就可以查看class文件的字节码指令,或者在 idea 中安装 jclass lib 插件,就可以直接查看字节码指令。

 

对应的字节码指令为:

 0 bipush 10  
 2 istore_1
 3 iload_1
 4 iinc 1 by 1
 7 istore_1
 8 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>
11 iload_1
12 invokevirtual #3 <java/io/PrintStream.println : (I)V>
15 return

然后,在网上搜索每个字节码表示的意思,就可以得到对应的解释。

 0 bipush 10     // 将10压入操作数栈中
 2 istore_1      // 将操作数栈中的10弹出,放在局部变量表序号为1的位置 
 3 iload_1       // 从局部变量表中加载序号为1的变量(10),压入操作数栈中
 4 iinc 1 by 1   // 将局部变量表中序号为1的变量的值加一
 7 istore_1      // 将操作数栈中的10弹出,放在局部变量表序号为1的位置 
 8 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>
11 iload_1       // 从局部变量表中加载序号为1的变量,用于输出打印
12 invokevirtual #3 <java/io/PrintStream.println : (I)V>
15 return

 

大致过程为,先从 局部变量表 中加载变量 i 到 操作数栈 ,然后对局部变量表中的 i 进行自增操作,最后再将操作数栈中的 i 返回给 局部变量表。这就解释了为什么执行了 i=i++ 之后,并没有改变 i 的值,同时验证了第一种解释,用一个临时变量保存 i 的值,然后 i 自增后再把临时变量的值返回给 i 。这里所说的保存 i 的值得临时变量就对应着 操作数栈。

 

 

个人想法:

操作数栈,顾名思义,应该堆变量进行操作的地方,自增不就是对数的一种操作,那为什么是直接在局部变量表中进行自增操作,反而操作数栈只是起到了一个 “暂时保存” 数据的地方。

不过仔细想,如果将自增操作放在操作数栈中进行的话,要想实现原本 i++ 先使用 i 的值再自增的效果就会更复杂一点,需要对局部变量表中的 i 进行两次压入栈的操作,上面的 i 用于使用 i 的值,等使用完之后弹出,先入栈的 i 在进行自增操作。

其实我感觉这个后自增,就有点像数据库中的脏读,也就是事务A读取了 i=10;然后事务B修改了 i=11,这个时候事务A有又将自己知道的 i=10赋值给数据库中。通常使用 i++ 时,在刚读取了局部变量表中的 i 的值后,i 的值就被修改了。也就是我们使用的 i 是脏数据(自增之前的数据)。

 

标签:10,操作数,字节,++,局部变量,自增,原理,变量
From: https://www.cnblogs.com/simply820/p/16871864.html

相关文章

  • CRC校验原理
    视频参考:​​http://v.youku.com/v_show/id_XMTA2Mzc5ODg=.html​​用到多项式和二进制数之间的转换然后用到的主要运算是模2除法 1、循环校验码(CRC码):是数据通信领域中最常......
  • 问题 J: 零基础学C/C++155——句子比较大小
    //本题考查的是二维数组的应用和strcmp和strcpy仅需对输入的字符串进行排序便可。点击查看代码#include<stdio.h>#include<string.h>#include<math.h>intmain(){......
  • C++第十二章:动态内存与类+断点错误
    前言C++类是针对对象的一种控制方法,可以看作各种函数与变量的管理方。类同样也会消耗内存,而且类一旦被创建,意味着相关成员会大量调用,此时内存的管理极其重要,常采用delete......
  • 通信协议:SPI协议的组成和基本原理
    SPI协议最早是由摩托罗拉公司提出的一种串行外围设备通信接口,是一种高速全双工的通信总线。它被广泛地使用在很多种产品中,比如ADC模块、LCD显示屏、存储器等设备中,包括一些......
  • OpenCV C++双目三维重建:双目摄像头实现双目测距
    OpenCVC++双目三维重建:双目摄像头实现双目测距目录​​OpenCVC++双目三维重建:双目摄像头实现双目测距​​​​1.目录结构​​​​2.依赖库​​​​3.双目相机标定​​​​......
  • c语言查漏补缺——Win32环境下动态链接库(DLL)编程原理
    在c语言查漏补缺总结了笔记,并分享出来。有问题请及时联系博主:​​Alliswell_WP​​,转载请注明出处。目录:一、Win32环境下动态链接库(DLL)编程原理1、导出和导入函数的匹配2......
  • 【数据结构】例题:表达式求值 C++实现
    先写一个链栈#pragmaonce///链栈的结点类型template<classDataType>classStackNode{public: DataTypedata; StackNode*next; StackNode(){ next=nul......
  • C++面经 ----- C++11新特性:左值右值
    概念左值:可以取地址并且有名字的东西就是左值。右值:不能取地址的没有名字的东西就是右值。纯右值:运算表达式产生的临时变量、不和对象关联的原始字面量、非引用返回......
  • C++ 面经 ----- C++11新特性:auto & decltype 类型推导
    C++11引入了auto和decltype关键字使用他们可以在编译期就推导出变量或者表达式的类型,方便开发者编码也简化了代码。 auto示例autoa=10;//10是int型,可以自动推导......
  • 使用一条for语句求若干个整数的平均值--C++自学
    #include<iostream>#include<stdlib.h>usingnamespacestd;intmain(){intx,count=0,sum=0;cout<<"输入若干整数:"<<endl;cin>>x;for(;x!=......