你真的了解 i++, ++i 和 i+++++i 以及 i+++i++ 吗?
我想大部分都知道 i++ 和 ++i的区别,i++ 就是先拿i来使用,之后再自增加1,而++i则是先自增加1,在拿i来使用,例如对于下面这两个语句,我敢保证大部分人都会做:
int i = 1;
System.out.println(i++)
int i = 1;
System.out.println(++1)
答案分别为 1,2。对于这个答案我猜大多数人都能答出来。不过 i++ 和 ++i 这两个操作,在内部是如何实现的呢?
我们先来看另外一个问题:
public static void main(String[] args) {
int i = 1;
System.out.println(i+++i++);
System.out.println(i);
}
这个比刚才那个难了点,答案分别是3,3。假如你对这个答案的由来了如指掌,那么你大不可必往下看,假如你不大理解或者想从底层的汇编指令的来了解这个操作,那么你可以看看我的解释。
首先我们先来看看 i++ 的题,主要是为了后面好解释点。
int i = 1;
System.out.println(i++);
这两行代码的部分汇编指令如下,注意,我只列出了几个重点的汇编语句:
ICONST_1 //把常量 1 加载到栈顶
ISTORE 1 //把栈顶的元素弹出,并赋值给局部变量表中位置为“1”的变量,此时指变量i。这两句就相当于 int i = 1; //接下来执行第二行代码
ILOAD 1 //把局部变量表中位置为“1”的变量加载到栈顶,即把i的值加载到栈顶
IINC 1 1 //直接把局部变量表中位置为“1”的变量加1,即把 i 加1。注意,这条指令并没有修改操作数栈就把 i 加1了。
INVOKEVIRTUAL java/io/PrintStream.println (I)V //把栈顶的元素打印出来,此时栈顶的元素是 1。所以打印的是 1
注:可以左右拉动
所以,此时打印的是1。
有些人可能没弄过汇编会有点蒙蔽,没事,我花个时间画个图来模拟(注:省略很多细节)。
刚开始时的局部变量表和操作数栈如图所示:
1、执行 ICONST_1,常量 1 进栈
2、执行 ISTORE 1,栈顶元素出栈存到位置“1”
3、执行 ILOAD 1,把位置“1”的变量值存到栈顶
4、执行 IINC 1 1 ,直接把局部变量表中位置为“1”的变量加 1
5、执行 INVOKEVIRTUAL java/io/PrintStream.println (I)V ,把栈顶的元素打印出来,此时栈顶的元素是 1.
所以虽然i已经等于2了,但此时栈顶的元素却是i之前的值 1 ,所以打印的是1。
这下关于 i ++ 的懂了吧?
那我们来看看 ++ i 与 i ++ 的汇编指令有什么不同。
int i = 1;
System.out.println(++i);
对应的部分重点汇编指令如下:
//和上面i++差不多,不过IINC 1 1 和ILOAD 1这两句的顺序调换了。
ICONST_1
ISTORE 1
IINC 1 1 //直接把局部变量表中位置为“1”的变量加1
ILOAD 1 //把位置“1”的变量压到栈顶,此时栈顶的元素是 2
INVOKEVIRTUAL java/io/PrintStream.println (I)V //所以打印的是2
再画下图演示一下:
1、执行了ICONST_1 和ISTORE 1这两句过后的局部变量和栈的情况如下
2、执行 IINC 1 1。注意,执行这条指令,操作数栈不会发生变化。
3、执行 ILOAD 1,把位置“1”的变量值压入栈顶
4、执行 INVOKEVIRTUAL java/io/PrintStream.println (I)V ,把栈顶的元素打印出来,此时栈顶的元素是 2
所以,对于 i++ 和 ++i的区别彻底懂了吧。
接下来我们来分析这个程序
int i = 1;
System.out.println(i+++i++);
System.out.println(i);
这里先说一下,按照运算符号的优先顺序,i+++i++等价于 (i++) + (i++)。
对应的部分汇编指令如下:
//第一行
ICONST_1
ISTORE 1
//第二行
ILOAD 1
IINC 1 1
ILOAD 1
IINC 1 1
IADD //把栈顶的两个元素弹出相加之后在把结果放回栈顶
INVOKEVIRTUAL java/io/PrintStream.println (I)V
//第三行
ILOAD 1
INVOKEVIRTUAL java/io/PrintStream.println (I)V
如果上面的那两个 i++ 和 ++i你看懂了,那么上面那个汇编应该也差不多能看懂。我用图来逐条分析一下吧。
1、执行了 ICONST_1 和ISTORE 1之后的状态如下
2、执行 ILOAD 1
3、执行 IINC 1 1
4、执行 ILOAD 1
5、执行 IINC 1 1。
此时实际上 i 的值已经是 3 了,只是栈顶放的都是 i 的旧值。
6、执行 IADD ,把栈顶两个元素出栈相加后再把结果入栈
7、执行INVOKEVIRTUAL java/io/PrintStream.println (I)V,此时栈顶元素为3,所以打印的是3
8、执行 ILOAD 1,把局部变量表加载到栈顶
9、执行INVOKEVIRTUAL java/io/PrintStream.println (I)V,此时栈顶元素为3,所以打印的是3
完毕
现在知道了把,对于 i+++++i 的题也知道怎么做以及怎么回事了吧。
这篇文章重点让你理解 i++ 与 ++ i的实现机制,对于上面的汇编指令以及进栈入栈的过程为了更好着说明要解决的问题,所以隐藏了很多细节,而且也删除了部分代码。如有错误的地方,还请见谅。
如果你想了解更多的汇编指令,我这里看到一篇总结的还挺全的:https://blog.csdn.net/hudashi/article/details/7062675
更多精彩文章可以关注我的公众号:苦逼的码农(ID:di201805),该公众号每周还会以专题的模式更新算法题,期待你的关注
推荐阅读:

你真的了解 i++, ++i 和 i+++++i 以及 i+++i++ 吗?的更多相关文章
- App你真的需要么
随着智能手机.移动路联网的普及,APP火的一塌糊涂,APP应用可谓五花八门,街上经常看到各种推广:扫码安装送东西,送优惠券.仿佛一夜之间一个企业没有自己的APP就跟不上时代了. 有时我在想:APP,你 ...
- [C#] C# 知识回顾 - 你真的懂异常(Exception)吗?
你真的懂异常(Exception)吗? 目录 异常介绍 异常的特点 怎样使用异常 处理异常的 try-catch-finally 捕获异常的 Catch 块 释放资源的 Finally 块 一.异常介 ...
- 你真的会玩SQL吗?之逻辑查询处理阶段
你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...
- SQL Server中SELECT会真的阻塞SELECT吗?
在SQL Server中,我们知道一个SELECT语句执行过程中只会申请一些意向共享锁(IS) 与共享锁(S), 例如我使用SQL Profile跟踪会话86执行SELECT * FROM dbo.T ...
- 您真的理解了SQLSERVER的日志链了吗?
您真的理解了SQLSERVER的日志链了吗? 先感谢宋沄剑给本人指点迷津,还有郭忠辉童鞋今天在QQ群里抛出的问题 这个问题跟宋沄剑讨论了三天,再次感谢宋沄剑 一直以来,SQLSERVER提供了一个非常 ...
- 你真的会玩SQL吗?和平大使 内连接、外连接
你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...
- 你真的会玩SQL吗?三范式、数据完整性
你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...
- 你真的会玩SQL吗?让人晕头转向的三值逻辑
你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...
- 你真的会玩SQL吗?EXISTS和IN之间的区别
你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...
- 你真的会玩SQL吗?无处不在的子查询
你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...
随机推荐
- 201771010126 王燕《面向对象程序设计(Java)》第十三周学习总结
实验十三 图形界面事件处理技术 实验时间 2018-11-22 1.实验目的与要求 (1) 掌握事件处理的基本原理,理解其用途: 事件源 (eventevent eventeventsource s ...
- 201771010126 王燕《面向对象程序设计(Java)》第十二周学习总结
实验十二 图形程序设计 实验时间 2018-11-14 1.实验目的与要求 (1) 掌握Java GUI中框架创建及属性设置中常用类的API: 创建空框架 . 在Java中,常采用框架(Frame) ...
- 201771010118 马昕璐 《面向对象程序设计(java)》第十三周学习总结
第一部分:理论知识学习部分 事件处理基础 1.事件源(event source):能够产生事件的对象都可以成为事件源.一个事件源是一个能够注册监听器并向监听器发送事件对象的对象. 2.事件监听器(ev ...
- echarts (geo/map) 渐变效果
这两天帮人搞了下中国范围内仓库量统计的需求,查了下echarts 里的文档找到类似的demo(链接:https://ecomfe.github.io/echarts-examples/public/e ...
- vue 值的更新
用了vue开发了一段时间,基本上感觉都是比较好用的一个框架,刚开始还是会存在一些小坑的东西,这里先浅谈下值的更新问题. 1.本组件的一些变量,或者是主view(路由页面)一些值,是通过vuex 中st ...
- 关于条件语句和 a && b
learn from javascript cookbook if (element == 'ab') array[index] = "**"; 该语句 等价于 if (eleme ...
- 访问不了虚拟机 ubuntu中的myql,解决方案
============================================================== 1. 现象 2. 原因分析 2.1 访问虚拟机中的mysql的前提: 你的 ...
- ubuntu connect to windows folder share
在windows上给远程登录的用户设置一个账号密码.”右击计算机图标“——"管理”——“本地用户和组”——“用户”.然后右击选择“新用户”,输入账号密码,并勾选“密码永不过期”,这样,在远程 ...
- 【原创开源应用第5期】基于RL-USB+RL-FlashFS的外挂U盘解决方案
说明:1.RL-USB外挂U盘的例子,最近太多网友咨询,再不做一个例子就说不过去了.此例子为此而生.2.RTX及其所有中间件基本都做例子了,就差这个USB Host功能了,这次算是补上,所有功能基本已 ...
- Netty的核心组件
Netty的主要组成模块: Channels Callbacks Futures Events 和 handlers 这些模块代表了不同类型的概念:资源,逻辑和通知.你的应用将会利用这些模块来获取网络 ...