显微镜下的i++与++i

2022-09-29 11:31 | 来源: IT之家     阅读量:9778  

注意,下面讨论的语言是Java。

显微镜下的i++与++i

这个问题网上很多文章都写的很烂,但是基本重复度很高看完之后感觉大部分都是错误的,对读者有误导

随便百度一下,先打开第一个。

上来先下结论。

先给运算赋值I++,比如a=i++,先给a=i赋值,再计算i=i+1,所以结果是a1。

+I先在赋值中运算,比如A = ++i,先i=i+1,再赋值a=i,所以结果是a2

然后举了很多例子来说明。

public class test 3 publicstaticvoidmainargs)inty = 0,//注意 "= "是赋值, " "等于。//这里y=++y首先对赋值y=++y进行运算,//y0,++ YY+1,结果是y = ++yy+10+11y = ++y,//y1,++yy+1,结果是y = ++yy+11+12y = ++y,//y2,++yy+1,结果是y = ++yy+12+13y = ++y,//y3,++ YY+1,结果是y = ++yy+13+14y = ++y,//y4,++ YY+1,结果是y = ++ YY+14+15 system . out . println( " y = "+y),//5 inti = 0,//i0,i++ 0,结果是i=i++(记得运算前赋值)I = i++,I = i++,I = i++,I = i++,I = i++,system . out . println( " I = "+I),//0 system . out . println( " "),//1

首先,这个例子不具有代表性,

其次,结论极具误导性。

但最重要的是,它无法帮助你真正理解i++和++i的本质是什么。

所以请听我说说i++和++I的区别。

先忘了什么是作业前作业。

别急,慢慢来,憋到最后。

++和++i字节代码

用javap命令查看字节码,或者直接用idea的插件,这里就不介绍了。

在方法中编写这样的代码。

publicfoidippinti = 1,i++,

检查它的字节码。

iconst _ 1 store _ 1 Inc 11 return

然后我们写这段代码。

publicfoidippinti = 1,++ I,

检查它的字节码。

iconst _ 1 store _ 1 Inc 11 return

不,完全一样。也就是说,当没有赋值操作时,i++和++i编译成字节码后,都是

iinc 1 1

同一个

之前有多少人明白i++和++i单独是有区别的。

看看iinc字节码的定义

找到官方的JVM手册

看到iinc字节码指令的格式是

iinc指数常数

Index表示局部变量表的索引,const表示其值要加多少。

所以以上

iinc 1 1

这意味着

将局部变量表索引到位置1处的值,加1。

这是在局部变量表的索引0位置,索引1位置的值由代码int i = 1设置,也就是1这个值加1就变成2

让我们回顾一下上面的代码。

publicfoidippinti = 1,i++,system . out . println(I),publicvoidppiinti = 1,++ I,system . out . println(I),

如果打印出I的值,很容易知道两者都是2。

所以很简单,i++和++i本身在字节码指令中体现为iinc,它只是简单地列出了I所在的局部变量的值,+1。

稍微复杂一点。

我们把上面的代码做的复杂一点,在++运算之后,重新赋值给I。

publicfoidippinti = 1,I = i++,publicvoidppiinti = 1,I = ++ I,

猜猜I的值是多少。

别急,再检查一下字节码。

void IPP—gt,I = i++,

iconst _ 1 store _ 1 load _ 1 Inc 11 store _ 1 return

void PPI—gt,I = ++ I,

iconst _ 1 store _ 1 Inc 11 load _ 1 store _ 1 return

这次不同,但是字节码指令都是一样的,只是顺序不同。

I = i++表示首先是iload_1,然后是iinc 1 1。

I = ++i表示先iinc 1 1,再iload_1。

所以也很简单在字节码指令中,i++和++i只有在最终赋给一个变量时才是不同的,而且只是按顺序

顺序的不同会导致什么让我们通过观察虚拟机堆栈来细化整个过程

观察虚拟机堆栈中的变化

但是首先你要知道虚拟机栈是什么,你要知道JVM的内存分区,这个我就不帮你复习了直接转到ipp方法进入虚拟机堆栈,以及这个方法堆栈框架中的初始结构

看,我= i++

ipp方法的字节码是分步执行的。

iconst _ 1 store _ 1 load _ 1 Inc 11 store _ 1 return

注意局部变量table 0表示这个,这里为了简单不写了,然后堆栈帧的frame这个字写错了,我任性,不改了。

Iconst_1:立即在堆栈上计数1。

Istore_1:操作数堆栈顶部—gt,局部变量表1位置

Ilo _ 1:局部变量表1位置—gt,操作栈顶

IIN11:局部变量table 1 position +1的值

Istore_1:操作数堆栈顶部—gt,局部变量表1位置

所以I的最后一个值,也就是局部变量表中1位置的值是1。

我们用动画再演示一遍。

你可以感觉到i = i++,iinc 1 1的写法完全没用,因为最后一个局部变量table 1的值会被最后一次赋值操作中操作数栈顶的值覆盖,所以前面的+1完全没用。

所以idea也会提醒你,这里的i++是没用的。

从不使用在i++中更改的值

再看看i = ++i

我相信你可以自己把这个推出去。

iconst _ 1 store _ 1 Inc 11 load _ 1 store _ 1 return

Iconst_1:立即在堆栈上计数1。

Istore_1:操作栈顶—gt,局部变量表1位置

IIN11:局部变量table 1 position +1的值

Ilo _ 1:局部变量表1位置—gt,操作栈顶

Istore_1:操作栈顶—gt,局部变量表1位置

所以I的最后一个值,也就是局部变量表中1位置的值是2。

直接用动画演示一下吧。

本质区别

所以看到本质区别是什么了吗。

不同的是

是 "先将值放入局部变量表+1,然后放入操作数堆栈 "

或者先将它放入操作数堆栈,然后在局部变量表中的值上加+1

仅此而已。/仅此而已/仅此而已

所以网上普遍说i++就是先赋值再运算。

赋值被推到操作数堆栈的顶部。

就是局部变量表+1运算。

反正我对不上这两个字。...

还有一种说法是,i++是先用I,再用+1,

也有人说,i++是先赋给自增的。

和......

嘿,兄弟,我们不要用自己的话误导读者好吗。

所以最后,用我的话说,我总结一个不含糊的。

++I:先把局部变量表中的I放入操作数栈,然后把局部变量表中的I值加到+1。

+I:先将I在局部变量表中的值+1,然后将I放入操作数堆栈。

做一些困难的事情。

当你从这个角度去理解的时候,你就可以做一个类似的复杂问题了,所以也就不在话下了如果是大事,可以在脑子里从头推导

看题目

inti = 2,inty = i++ ++ I,y=inta = 2,a = a++ ++ a,a=intb = 2,b = b++++(b++ = 2),b=

回答

y = 6

a = 6

b = 18

你做得对吗。

我展示了最困难问题的字节码。

根据黄色的字,你可以改变操作数栈自己编动画我不想偷懒

intb = 2,b = b++++(b++ = 2),iconst _ 2,操作栈2i store _ 1,局部变量表b = 2i load _ 1,操作数堆栈2 Inc 1 by 1,局部变量表b = 3 Inc 1 by 1,局部变量表b = 4 iload _ 1,操作数堆栈24 iinc 1 by 1,局部变量表b = 5i load _ 1,操作数堆栈245iadd操作栈29(= 4+5)iadd,操作数堆栈11(= 2+9)iinc 1 by 2,局部变量表b = 7i load _ 1,操作栈117iadd操作数堆栈18(= 11+7)istore _ 1,局部变量表b=18

不管有多难,我都觉得有点无聊。大家自己出题吧~

如果你对这里的堆叠顺序感到困惑,例如,你不认为添加数学是第一步吗为什么不先入栈参与操作呢

其实这个和i++和++I的知识无关,你需要知道的是前缀,中缀,后缀表达式这里只举个例子,不解释

简单来说,就是如何把数学表达式转换成一种格式按照这个顺序,通过栈实现计算就很方便了

例如

b++++(b++ = 2)

在转换为后缀表达式的过程中,++运算完全不受影响,因此简化为

b + + b

转换成后缀表达式后,它是

b b b + + b +

按照这个顺序,堆栈就是字节码中指令的顺序比如堆栈B是iload_1,堆栈操作符是iadd

而这里每个B的值就是压叠瞬间B的值。

最后愤怒的说两句。

所以网上关于++的题目其实是两个知识点。

i++和++i参与运算时的字节码指令

将数学表达式转换成堆栈运算的后缀表达式。

网上的大部分解释都不是来自最直接的字节码指令,我认为混淆两个知识点是不负责任的。

回头看看文章开头的结论。

先给运算赋值I++,比如a=i++,先给a=i赋值,再计算i=i+1,所以结果是a1。

+I先在赋值中运算,比如A = ++i,先i=i+1,再赋值a=i,所以结果是a2

先不说它不用字节码来说明问题有没有发现这种说法本身就是错误的,非常误导人

先赋值a=i,然后计算i=i+1。

其实根本没有赋值,只是把I扔进操作数栈等待被操作,然后局部变量表i=i+1,最后操作数栈里的I弹出栈,写入局部变量表里A的位置这叫赋值

总之,这类文章还是少看为好。

免责声明:该文章系本站转载,旨在为读者提供更多信息资讯。所涉内容不构成投资、消费建议,仅供读者参考。

Powered by 网站地图 | RSS订阅

Copyright © 2018 All Rights Reserved 火星区块 hot.hjcll.Cn 版权所有

邮箱:jokerdeyouxiang@sina.com