我想大部分都知道 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),该公众号每周还会以专题的模式更新算法题,期待你的关注

推荐阅读:

【算法实战】生成窗口最大值数组

谈谈NAT:什么?全球IP和私有IP是什么鬼?

你真的了解 i++, ++i 和 i+++++i 以及 i+++i++ 吗?的更多相关文章

  1. App你真的需要么

    随着智能手机.移动路联网的普及,APP火的一塌糊涂,APP应用可谓五花八门,街上经常看到各种推广:扫码安装送东西,送优惠券.仿佛一夜之间一个企业没有自己的APP就跟不上时代了. 有时我在想:APP,你 ...

  2. [C#] C# 知识回顾 - 你真的懂异常(Exception)吗?

    你真的懂异常(Exception)吗? 目录 异常介绍 异常的特点 怎样使用异常 处理异常的 try-catch-finally 捕获异常的 Catch 块 释放资源的 Finally 块 一.异常介 ...

  3. 你真的会玩SQL吗?之逻辑查询处理阶段

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  4. SQL Server中SELECT会真的阻塞SELECT吗?

    在SQL Server中,我们知道一个SELECT语句执行过程中只会申请一些意向共享锁(IS) 与共享锁(S), 例如我使用SQL Profile跟踪会话86执行SELECT * FROM dbo.T ...

  5. 您真的理解了SQLSERVER的日志链了吗?

    您真的理解了SQLSERVER的日志链了吗? 先感谢宋沄剑给本人指点迷津,还有郭忠辉童鞋今天在QQ群里抛出的问题 这个问题跟宋沄剑讨论了三天,再次感谢宋沄剑 一直以来,SQLSERVER提供了一个非常 ...

  6. 你真的会玩SQL吗?和平大使 内连接、外连接

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  7. 你真的会玩SQL吗?三范式、数据完整性

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  8. 你真的会玩SQL吗?让人晕头转向的三值逻辑

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  9. 你真的会玩SQL吗?EXISTS和IN之间的区别

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  10. 你真的会玩SQL吗?无处不在的子查询

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

随机推荐

  1. C#代码总结03---通过获取类型,分类对前台页面的控件进行赋值操作

    该方法: 一般用于将数据库中的基本信息字段显示到前台页面对应的字段控件中 private void InitViewZc(XxEntity model) { foreach (var info in ...

  2. 【转】 web前端开发分享-目录

    http://www.cnblogs.com/jikey/p/3613082.html 1. web前端开发分享-css,js入门篇 2. web前端开发分享-css,js进阶篇 3. web前端开发 ...

  3. python-MYSQL(包括ORM)交互

    1.首先,我们必须得连上我们的MYSQL数据库.个人遇到连不上MYSQL数据的问题主要有:数据库的权限问题.数据库表权限的问题 同时获取数据库中的数据等. //==================== ...

  4. Python-写文件

    写文件需要三步:打开文件写入内容关闭文件 写入内容一般要选择打开的模式:f = open('out.txt','w')此处的w就是writing,代表以写入文件的模式打开,原文件里的内容会被新写入覆盖 ...

  5. c#提交事务的两种方法

    1. using (TransactionScope ts = new TransactionScope()) { 除非显示调用ts.Complete()方法.否则,系统不会自动提交这个事务.如果在代 ...

  6. Java-IO流之转换流的使用和编码与解码原理

    一.理论: 1.字符流和字节流区别是什么? 字符流=字节流+编码集,在实际读取的时候其实字符流还是按照字节来读取,但是会更具编码集进行查找编码集字典解析相应的字节,使得一次读取出一个字符: 2.什么是 ...

  7. 【ASP】session实现购物车

    1.问题提出 利用session内置对象,设计并实现一个简易的购物车,要求如下: 1)利用用户名和密码,登录进入购物车首页 2)购物首页显示登录的用户名以及该用户是第几位访客.(同一用户的刷新应该记录 ...

  8. mysqldump 导出中文乱码

    命令:mysqldump -uroot -p test > /data/test.sql 导出后的数据库打开是乱码,如下: 开始以为打开的方式不对,就用记事本打开后,用utf-8的编码格式另保存 ...

  9. 【RL-TCPnet网络教程】第4章 RL-TCPnet网络协议栈简介

    第4章        RL-TCPnet网络协议栈简介 本章节介绍RL-TCPnet网络协议栈,让大家对 RL-TCPnet有一个整体的了解,RL-TCPnet是一款小型网络协议栈,适用于 ARM 内 ...

  10. 什么是HTML?HTML5是什么?HTML5有那些优势和特性?

    一.什么是HTML 在了解html5之前,首先要说一下html语言,尽管是更新后的5,但很多的地方还是保留了html的优势. HTML是HyperText Markup Language超级文本标记语 ...