<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          顯微鏡下的 i++ 與 ++i

          共 4762字,需瀏覽 10分鐘

           ·

          2021-01-14 14:11

          注意,以下討論的語(yǔ)言是 Java

          這個(gè)問(wèn)題被網(wǎng)上的好多文章寫爛了,但基本重復(fù)度很高,我看過(guò)后的感覺(jué)是,大部分都是錯(cuò)誤的、誤導(dǎo)讀者的。

          隨便百度一下,我們打開(kāi)第一條。

          上來(lái)先說(shuō)個(gè)結(jié)論

          i++ 先賦值在運(yùn)算,例如 a=i++,先賦值a=i,后運(yùn)算i=i+1,所以結(jié)果是a==1
          ++i 先運(yùn)算在賦值,例如 a=++i,先運(yùn)算i=i+1,后賦值a=i,所以結(jié)果是a==2

          然后給了成噸的例子來(lái)說(shuō)明

          public?class?Test3?{
          ?public?static?void?main(String[]?args)?{
          ??int?y=0;?
          ??//注意"="是賦值,"=="才是相等
          ??//這里的y=++y?是先運(yùn)算在賦值
          ??y=++y;//?y==0,++y==y+1;?結(jié)果y=++y?==?y+1?==?0+1?==1
          ??y=++y;//?y==1,++y==y+1;?結(jié)果y=++y?==?y+1?==?1+1?==2
          ??y=++y;//?y==2,++y==y+1;?結(jié)果y=++y?==?y+1?==?2+1?==3
          ??y=++y;//?y==3,++y==y+1;?結(jié)果y=++y?==?y+1?==?3+1?==4
          ??y=++y;//?y==4,++y==y+1;?結(jié)果y=++y?==?y+1?==?4+1?==5
          ??System.out.println("y="+y);//5
          ??int?i?=0;
          ??//?i==0,i++==0;?結(jié)果i=i++?==?(記住先賦值后運(yùn)算)
          ??i=i++;
          ??i=i++;
          ??i=i++;
          ??i=i++;
          ??i=i++;
          ??System.out.println("i="+i);//0
          ??System.out.println("================");//1
          ?}
          }

          首先這個(gè)例子沒(méi)有任何代表性;

          其次得出的結(jié)論也是極其誤導(dǎo)人的;

          但最關(guān)鍵的是,這無(wú)法幫助你真正理解 i++ 和 ++i 的本質(zhì)是什么

          所以 i++ 和 ++i 的區(qū)別請(qǐng)聽(tīng)我說(shuō)

          先忘掉什么“先賦值、后運(yùn)算”

          別著急,慢慢來(lái),忍住看到最后

          i++ 和 ++i 字節(jié)碼

          查看字節(jié)碼用 javap 命令,或者直接用 idea 的插件,這里不做過(guò)多介紹

          在某方法里寫上這樣一段代碼

          public?void?ipp()?{
          ????int?i?=?1;
          ????
          i++;
          }

          查看其字節(jié)碼

          iconst_1
          istore_1
          iinc?1?1
          return

          然后我們?cè)趯懮线@樣一段代碼

          public?void?ipp()?{
          ????int?i?=?1;
          ????
          ++i;
          }

          查看其字節(jié)碼

          iconst_1
          istore_1
          iinc?1?1
          return

          發(fā)現(xiàn)沒(méi),完全一樣。也就是說(shuō),在沒(méi)有賦值操作時(shí),i++ ++i 編譯成字節(jié)碼后,都是

          iinc 1 1

          完全一樣

          有多少人之前的理解是 i++ 和 ++i 本身孤零零地放在那是有區(qū)別的呢?

          看 iinc 字節(jié)碼的定義

          找到 JVM 官方手冊(cè)

          https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.iinc

          看到 iinc 字節(jié)碼指令的格式為

          iinc index const

          index 表示局部變量表的索引,const 表示將其值加上多少

          所以上面的

          iinc 1 1

          就表示

          將局部變量表索引為1位置的值,加上1

          局部變量表索引 0 位置處是 this,索引 1 位置處的值,是int i = 1 這段代碼設(shè)置的,也就是 1。把這個(gè)值加 1,就變成了了 2

          再來(lái)回顧下上面的代碼

          public?void?ipp()?{
          ????int?i?=?1;
          ????i++;
          ????System.out.println(i);
          }

          public?void?ppi()?{
          ????int?i?=?1;
          ????++i;
          ????System.out.println(i);
          }

          如果打印 i 的值,很容易知道,兩個(gè)都是 2

          所以很簡(jiǎn)單,i++ 和 ++i 本身在字節(jié)碼指令中的體現(xiàn)都是?iinc,就是單純把 i 所在的局部變量表那個(gè)位置的值,+1

          稍稍復(fù)雜一點(diǎn)

          我們把上面的代碼稍稍復(fù)雜一點(diǎn),++操作后,再重新賦值給 i

          public?void?ipp()?{
          ????int?i?=?1;
          ????i?=?i++;
          }

          public?void?ppi()?{
          ????int?i?=?1;
          ????i?=?++i;
          }

          你猜 i 的值分別是多少

          別急,再次查看字節(jié)碼

          void ipp() --> i = i++;

          iconst_1
          istore_1
          iload_1
          iinc?1?1

          istore_1
          return

          void ppi() --> i = ++i;

          iconst_1
          istore_1
          iinc?1?1
          iload_1

          istore_1
          return

          這回看到不一樣了,但字節(jié)碼指令都相同,只是順序不同

          i = i++ 就是 先 iload_1 iinc 1 1

          i = ++i 就是 先 iinc 1 1 iload_1

          所以也很簡(jiǎn)單,i++ 和?++i 只有在最終賦值給某變量時(shí)(實(shí)際上是因?yàn)閰⑴c了運(yùn)算,因?yàn)橹苯淤x值也是一種無(wú)運(yùn)算符號(hào)的運(yùn)算),字節(jié)碼指令是不同的,而且也只是順序上的不同。

          那順序的不同會(huì)導(dǎo)致結(jié)果怎樣呢?下面我們通過(guò)觀察?虛擬機(jī)棧?來(lái)細(xì)化整個(gè)過(guò)程

          觀察虛擬機(jī)棧中的變化

          但你得先知道虛擬機(jī)棧是什么,也就得知道 JVM 的內(nèi)存劃分,這塊就不幫你復(fù)習(xí)了哈,直接上 ipp() 方法入虛擬機(jī)棧后,這個(gè)方法棧幀里的初始構(gòu)造。

          看 i = i++

          下面一步步執(zhí)行 ipp() 方法的字節(jié)碼

          iconst_1
          istore_1
          iload_1
          iinc?1?1
          istore_1
          return

          注意局部變量表 0 處表示的是 this,這里為了簡(jiǎn)化沒(méi)有寫出,然后棧幀的“幀”字寫錯(cuò)啦,我就任性一下不改了哈
          iconst_1:將立即數(shù) 1 壓棧

          istore_1:操作數(shù)棧頂 -> 局部變量表 1 位置
          iload_1:局部變量表 1 位置 -> 操作數(shù)棧頂

          iinc 1 1:局部變量表 1 位置的值 +1

          istore_1:操作數(shù)棧頂 -> 局部變量表 1 位置

          所以,最后 i 的值,也就是局部變量表中 1 位置處的值,就是 1

          我們用動(dòng)畫再演示一遍

          你可以感受到,i = i++ 這種寫法,iinc 1 1 這一步是完全沒(méi)有用的,因?yàn)樽詈缶植孔兞勘?1 位置處的值,在最后一步賦值操作時(shí),會(huì)被操作數(shù)棧頂處的值覆蓋,所以之前的 +1 完全沒(méi)用

          所以 idea 也會(huì)提示你,這里的 i++ 沒(méi)用

          the value changed at 'i++' is never used

          再看 i = ++i

          相信這個(gè)你自己也可以推到出來(lái)了
          iconst_1
          istore_1
          iinc?1?1
          iload_1
          istore_1
          return
          • iconst_1:將立即數(shù) 1 壓棧
          • istore_1:操作棧頂 -> 局部變量表 1 位置
          • iinc 1 1:局部變量表 1 位置的值 +1
          • iload_1:局部變量表 1 位置 -> 操作棧頂
          • istore_1:操作棧頂 -> 局部變量表 1 位置

          所以,最后 i 的值,也就是局部變量表中 1 位置處的值,就是 2

          我們直接用動(dòng)畫演示一遍

          本質(zhì)區(qū)別

          所以看出本質(zhì)區(qū)別是什么了么?

          區(qū)別就是

          是?"先把局部變量表中的值 +1,再放到操作數(shù)棧中"

          還是 "先放到操作數(shù)棧中,再把局部變量表中的值 +1"

          僅此而已

          所以網(wǎng)上普遍的說(shuō)法,i++ 表示 先 賦值運(yùn)算

          • 賦值就是 壓入操作數(shù)棧頂
          • 運(yùn)算就是?局部變量表 +1 操作

          反正這倆詞我是對(duì)應(yīng)不上...

          還有的說(shuō)法是,i++ 是先把 i 拿出來(lái)使用,然后再+1;

          還有的說(shuō)法是,i++?先賦值在自增

          還有的 ... ...

          哥誒,咱別用自己造的詞誤導(dǎo)讀者了好不?

          所以最后用我的話總結(jié)一個(gè)沒(méi)有任何歧義的

          • i++:先將局部變量表中的 i 放入操作數(shù)棧中,再將局部變量表中的 i 值 +1
          • ++i:先局部變量表中的 i 值 +1,再將i放入操作數(shù)棧中

          來(lái)點(diǎn)難的

          當(dāng)你從這個(gè)角度理解了之后,再做類似的復(fù)雜一點(diǎn)的題,也不在話下,大不了在腦子里從頭推導(dǎo)一遍即可

          看題

          int?i?=?2;
          int?y?=?i++?+?++i;
          y?=??

          int?a?=?2;
          a?=?a++?+?++a;
          a?=??

          int?b?=?2;
          b?=?b++?+?(++b?+?++b)?+?(b?+=?2);
          b?=??



          答案

          y = 6

          a = 6

          b = 18

          你做對(duì)了么?

          我把最難的那個(gè)題的字節(jié)碼展示出來(lái)

          按照黃色的字可以到操作數(shù)棧的變化(從左到右就是操作數(shù)棧從棧底到棧頂),自己腦補(bǔ)一下動(dòng)畫吧,不想做了有點(diǎn)懶哈哈哈~

          int?b?=?2;
          b?=?b++?+?(++b?+?++b)?+?(b?+=?2);

          iconst_2????;操作數(shù)棧?2
          istore_1????;局部變量表?b=2
          iload_1?????;操作數(shù)棧?2
          iinc?1?by?1?;局部變量表?b=3
          iinc?1?by?1?;局部變量表?b=4
          iload_1?????;操作數(shù)棧?2?4
          iinc?1?by?1?;局部變量表?b=5
          iload_1?????;操作數(shù)棧?2?4?5
          iadd????????;操作數(shù)棧?2?9(=4+5)
          iadd????????;操作數(shù)棧?11(=2+9)
          iinc?1?by?2?;局部變量表?b=7
          iload_1?????;操作數(shù)棧?11?7
          iadd????????;操作數(shù)棧?18(=11+7)
          istore_1????;局部變量表?b=18

          再難的,我覺(jué)得就有些無(wú)聊了,大家自己給自己出題吧~

          如果你對(duì)這里的入棧順序有困惑,比如你感覺(jué)加了()數(shù)學(xué)上不是先進(jìn)行運(yùn)算么?怎么不是先入棧參與運(yùn)算呢?

          那其實(shí)這和 i++ 與 ++i?的知識(shí)就不相關(guān)了,你需要了解的是 前綴、中綴、后綴表達(dá)式,這里只舉個(gè)例子不展開(kāi)講解。

          簡(jiǎn)單說(shuō)就是如何將數(shù)學(xué)表達(dá)式,轉(zhuǎn)換成一種格式,按照這個(gè)順序可以方便通過(guò)棧來(lái)實(shí)現(xiàn)計(jì)算。

          比如

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

          在轉(zhuǎn)成后綴表達(dá)式過(guò)程中 ++ 操作根本不受影響,先簡(jiǎn)化成

          b + (b + b) + b

          轉(zhuǎn)換成后綴表達(dá)式后就是

          b b b + + b +

          照著這個(gè)順序壓棧,就是字節(jié)碼中指令的順序啦,比如 b 壓棧就是 iload_1,運(yùn)算符(+)壓棧就是 iadd,再回過(guò)去證明一下哦~

          而這里的每一個(gè) b 的值,就是壓棧那一時(shí)刻的 b 的值

          最后憤怒地再說(shuō)兩句

          所以,網(wǎng)上關(guān)于 ++?的題目,其實(shí)是兩個(gè)知識(shí)點(diǎn)

          • i++ 與 ++i 參與運(yùn)算時(shí)的字節(jié)碼指令

          • 將數(shù)學(xué)表達(dá)式轉(zhuǎn)換為棧操作的后綴表達(dá)式

          而網(wǎng)上的講解,大部分都不是從最直接的字節(jié)碼指令說(shuō),還將兩個(gè)知識(shí)點(diǎn)混為一談,我覺(jué)得是不負(fù)責(zé)任的。

          回過(guò)頭來(lái)再看開(kāi)頭說(shuō)的那篇文章的結(jié)論

          i++ 先賦值在運(yùn)算,例如 a=i++,先賦值a=i,后運(yùn)算i=i+1,所以結(jié)果是a==1
          ++i 先運(yùn)算在賦值,例如 a=++i,先運(yùn)算i=i+1,后賦值a=i,所以結(jié)果是a==2
          先不說(shuō)它沒(méi)有用字節(jié)碼來(lái)說(shuō)明問(wèn)題,你有沒(méi)有發(fā)現(xiàn)這說(shuō)的本身就是錯(cuò)的,有很大的誤導(dǎo)性。
          先賦值a=i,后運(yùn)算i=i+1
          其實(shí)根本沒(méi)有先賦值吧,只是把 i 丟到操作數(shù)棧中等待被運(yùn)算而已,然后局部變量表 i=i+1,最后操作數(shù)棧中的 i 出棧并寫入局部變量表中 a 的位置,這時(shí)才叫賦值。
          總之,這類文章還是少看為好


          瀏覽 22
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产一级免费观看视频 | 成人影音先锋AV | 亚洲永久免费黄色电影 | 操老逼视频 | 欧美性受XXXX黑人XYX性 |