前言:
之前有朋友跟我討論過一個問題,他問在java中,i=1;i=i++;之后的結果時什么,我一開始認為是2,但當他說答案是1的時候,我不得不想一個合理的解釋,我想,可能是因為臨時變量,當i賦值給i后,自增更改了臨時變量,所以i值并沒有改變。當我在為我的解釋表示滿意時,他們說在c語言下的答案是2,好吧,我只能說是編譯器的實現不同,當然這個答案太沒說服力了,所以尋找答案的任務就開始了。
我先在《c++ primer》上看見這樣的解釋:“++i的效率要比i++的效率高,因為i++必須要有一個臨時變量來存放i本身的值“,那么我之前只說對了臨時變量,但原因是臨時變量先用來存放i的值,然后i本身自增,之后,臨時變量賦值給左邊,這時,i的值又被 自己之前的值覆蓋了,所以又變成1,但這只是個推測,再說c語言中為什么是2?所以不得不反編譯了。。。
java的jdk自帶一個工具,javap,可以反編譯,我們用它java編譯器生成的“jvm版匯編碼”,我會寫下兩段代碼用來做對比,一種是i=i++;一種常見的j=i++;注意,我使用2作為初始值(也就是說它會被自增為3),因為1太特殊了,它在字節碼中的變量名隨處可見,而且++不是加1么,所以我用2做初值,也方便查找。
publicclassTest{publicstaticvoidmain(String[] args){inti =2; i = i++; System.out.println(i); }}
javap -c Test 的字節碼如下:
public class Test extends java.lang.Object{public Test(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object.“《init》”:()V 4: returnpublic static void main(java.lang.String[]); Code: 0: iconst_2 1: istore_1 2: iload_1 3: iinc 1, 1 6: istore_1 7: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 14: return}
0: iconst_2 //將int型的常量2壓棧1: istore_1 //將int型值存入變量1中
就是定義并初始化i的值。
關鍵是這里:
2: iload_1 //保存變量1的值3: iinc 1, 1 //將變量1的值自增(i現在是3了)6: istore //將之前的保存值放入變量i中(i現在又變成2了。。)
其實在2: iload_1 時,i變量的值被放在了棧中,這就是我們說的臨時存儲。。之后,又被賦了過來
看看Test2,你就會明白正常的運作:
publicclassTest2{publicstaticvoidmain(String[] args){inti =2;intj =0; j = i++; System.out.println(j); }}
javap -c Test2 的字節碼如下:
public class Test2 extends java.lang.Object{public Test2(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object.“《init》”:()V 4: returnpublic static void main(java.lang.String[]); Code: 0: iconst_2 1: istore_1 2: iconst_0 3: istore_2 4: iload_1 5: iinc 1, 1 8: istore_2 9: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 12: iload_2 13: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 16: return}
4: iload_1 //變量1的值被保存(入棧)5: iinc 1,1 //i自增8 istore_2 // 彈棧,賦值給變量2.。。
現在明白了吧,,到底發生了什么。。
但是還沒完,,,,,我們還沒解釋在c語言中發生了什么。。。
在gcc中,編譯一個c程序要四個階段,預處理,編譯,匯編,鏈接。我們在編譯階段后停下,這樣會得到AT&T的匯編碼,
我們寫下兩種代碼,類似于java版
先看正常情況:
#include《stdio.h》intmain(){inti =2;intj =0; j = i++;printf(“j = %d
”,j);return0;}
編譯成匯編碼:
.file“test.c” .section .rodata.LC0:.string“j = %d
” .text.globl main.typemain, @functionmain: pushl %ebp movl %esp, %ebp andl $-16, %espsubl$32, %espmovl$2, 28(%esp)movl$0, 24(%esp) movl 28(%esp), %eax movl %eax, 24(%esp)addl$1, 28(%esp) movl $.LC0, %eax movl 24(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp)callprintfmovl$0, %eax leave ret .size main, 。-main.ident“GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3”.section.note.GNU-stack,“”,@progbits
包括 movl $.LC0, %eax 和之后語句的都和輸出有關了。
我們再把初始化有關的除去,關鍵的部分是:
movl 28(%esp), %eaxmovl %eax, 24(%esp)addl$1, 28(%esp)
保存了i的值,(放在eax中),之后又放入j中,i再自增,這就很符合大家心里的答案,那么如果是i=i++時自然就是i被賦值,再自增。。。也就是大家心中的答案了,但是真是這樣么?我們繼續看。。。。
#include《stdio.h》intmain(){inti =2;i = i++;printf(“i = %d
”,i);return0;}
對應的匯編碼:
.file“test2.c” .section .rodata.LC0:.string“i = %d
” .text.globl main.typemain, @functionmain: pushl %ebp movl %esp, %ebp andl $-16, %espsubl$32, %espmovl$2, 28(%esp)addl$1, 28(%esp) movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp)callprintfmovl$0, %eax leave ret .size main, 。-main.ident“GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3”.section.note.GNU-stack,“”,@progbits
關鍵部分就省一句了 addl $1, 28(%esp)這簡直就是。。。。看我的test3.c吧。。。
#include《stdio.h》intmain(){inti =2;i++;printf(“i = %d
”,i);return0;}
產生的匯編碼:
aiqier@aiqier-laptop:~/c/test3$ cat test3.s
.file“test3.c” .section .rodata.LC0:.string“i = %d
” .text.globl main.typemain, @functionmain: pushl %ebp movl %esp, %ebp andl $-16, %espsubl$32, %espmovl$2, 28(%esp)addl$1, 28(%esp) movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp)callprintfmovl$0, %eax leave ret .size main, 。-main.ident“GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3”.section.note.GNU-stack,“”,@progbits
恩,在c語言中i=i++;居然跟i++;的匯編代碼一樣,按正常的邏輯(我們之前的分析),i=2;i=i++;i的值是3,已經沒問題了,但是gcc會對編譯器做一些優化,所以保存i的值,在賦值給i,這兩條語句自然是雞肋了,就被優化掉了。
總結,我們發現對于i++保存的臨時值,java中是自增在返回臨時值,而c語言中是返回臨時值后,在自增,所以這就是為什么i=i++在這兩種語言中不同的結果了,c#我就不知道了,有興趣的同學可以試試,python么,沒有++啦,呵呵,如果我的分析有什么不對之初,希望能與你一起探討。
編輯:jq
-
C語言
+關注
關注
180文章
7605瀏覽量
136903 -
編譯器
+關注
關注
1文章
1634瀏覽量
49139 -
JVM
+關注
關注
0文章
158瀏覽量
12234
原文標題:C語言 & Java 解析:當 i = i++ 后,結果究竟是什么?
文章出處:【微信號:xx-cyy,微信公眾號:C語言編程基礎】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論