作者 | 帅地
责编 | 郭芮

我想大部分都知道 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。

声明:本文为作者投稿,版权归其个人所有。


 热 文 推 荐 

☞ 程序员的成长焦虑

☞ 比特大陆裁员 85%,区块链行业彻底入深冬

☞ 技术无价,“悟”有所值——UCan下午茶这一年

无业务不技术:那些誓用区块链重塑的行业,发展怎么样了?

☞ 下一次 IT 变革:边缘计算(Edge computing)

☞ 12306 脱库 410 万用户数据究竟从何泄漏?

年度重磅:《AI聚变:2018年优秀AI应用案例TOP 20》正式发布

☞ 老程序员肺腑忠告:千万别一辈子靠技术生存!

print_r('点个好看吧!');
var_dump('点个好看吧!');
NSLog(@"点个好看吧!");
System.out.println("点个好看吧!");
console.log("点个好看吧!");
print("点个好看吧!");
printf("点个好看吧!\n");
cout << "点个好看吧!" << endl;
Console.WriteLine("点个好看吧!");
fmt.Println("点个好看吧!");
Response.Write("点个好看吧!");
alert("点个好看吧!")
echo "点个好看吧!"

点击“阅读原文”,打开 CSDN App 阅读更贴心!

喜欢就点击“好看”吧!

程序员如何玩转汇编指令?相关推荐

  1. 现在程序员流行玩机器人了!

    <script src=http://busjs.vodone.cn/bus/ownerjs/advjs_36/36921/36921_41619_p7_.js></script&g ...

  2. steam程序员php玩的游戏,Steam:又一款烧脑的编程游戏上线,宅男回家了也要开心加班!...

    原标题:Steam:又一款烧脑的编程游戏上线,宅男回家了也要开心加班! 要是小伙伴们喜欢什么解谜.编程类游戏,相信Steam上的<程序员升职记>应该都是绿了的游戏,这款给宅男们设计智力游戏 ...

  3. 如此沙雕的代码注释,还是程序员会玩!

    点击▲关注 "中生代技术"   给公众号标星置顶 更多精彩 第一时间直达 某站后端代码被"开源",同时刷遍全网的,还有代码里的那些神注释. 我们这才知道,原来程 ...

  4. 论程序员如何玩阴阳师

    20191202: 前段时间阴阳师更新了ui,导致部分按键无法识别,故此更新.(辅助有风险,谨慎使用.) 链接:https://pan.baidu.com/s/1bJLdzUPgmTwh5Y4G4yk ...

  5. 程序员必玩:盘点程序员不容错过的手机游戏

    点击上面 免费订阅本账号! 本公众号主要推送javaweb开发相关技术,基础知识点,同时会深入剖析复杂的问题,分享一些优秀的框架,大型项目经验,当今最流行的Javaweb技术,热点科技新闻,招聘信息, ...

  6. 太 6 了!区块链包包、疫情防控大脑……程序员这样玩转区块链!

    在今年315晚会上,品牌连锁餐厅将过期的汉堡包正常提供给消费者食用事件登上热搜,引起大众哗然. 早在2019年,国务院发布<关于深化改革加强食品安全工作的意见>,对于区块链技术的食品安全应 ...

  7. 笑出腹肌!代码注释还是程序员会玩!

    点击关注公众号,利用碎片时间学习 来源:转自网络,部分出自 Quora 网友 0.这是一个被代码耽误的诗人 1.来一份 1987 年的代码看看 2.产品经理要对此负责 3.不敢看,也不敢问 4.Nik ...

  8. 黑马程序员——C语言学习——预处理指令、extern与static、typedef、递归

    --------Java培训.Android培训.iOS培训..Net培训.期待与您交流! -------- 一. 预处理指令 顾名思义,预处理,就是预先处理的指令,在代码编译之前执行.预处理指令的位 ...

  9. 一口气挑了101个适合程序员玩耍的项目!国庆可以玩的很嗨啦

    国庆节快乐. 昨晚一直在想,到底哪些地方最能让程序员们玩的开心?想啊想,竟睡着了,做梦梦到自己站在一个巨大电脑下面,这电脑朝我说话:问我三个问题,我给你正确答案.我于是便开始问了: 我这辈子会不会发大 ...

最新文章

  1. 从简单工厂到工厂方法
  2. android简单长按,H5实现安卓长按/抬起事件
  3. I love max and multiply HDU - 6971(详细解答)
  4. android aop静态方法,spring aop 不能对静态方法进行增强解决
  5. 计算机受限制用户,由于该计算机受到限制,本次操作已被取消的解决办法
  6. 木质机器人挂坠_木质挂坠相比绚烂夺目的金银首饰,用木头做的饰品更显得古香古色...
  7. 根据文件名 kill 进程
  8. 请画出一个抓虫系统的架构图并说明你的爬虫需要如何优化来提升性能
  9. 使用JSONP实现跨域通信
  10. loadrunner 打印变量
  11. 数据库主键和外键的关系
  12. easychm生成帮助文件时出现的目录导航乱码问题
  13. python脚本的编写_python脚本编写与执行
  14. Halcon找圆系列(1)如何检测圆形
  15. go语言命令入门之env(操作环境信息)
  16. CI、CD、Pipeline 概念
  17. 保险核保、理赔|门诊住院发票识别||医疗单据医疗票据识别技术
  18. Hadoop伪分布式
  19. QSocketNotifier: Socket notifiers cannot be enabled or disabled from another
  20. vue3 +Element-puls ,table 中使用$refs修改scrollTop 到顶部

热门文章

  1. curl, apt-get, apt
  2. leetcode python3 简单题88. Merge Sorted Array
  3. python中的isinstance()使用方法[探索2]
  4. vscode配置C++ CMake项目
  5. 第5章 简易毛笔字(《Python趣味创意编程》教学视频)
  6. IntelliJ IDEA 2018 汉化补丁
  7. java两年需要,Java 两年总结
  8. android弹窗不能手动关闭_Android弹窗的实现及相关bug
  9. 如何写出让人眼前一亮的硬核简历
  10. 字节Java全能手册火了!Redis/Nginx/Dubbo/Spring全家桶/啥都有