++ 運算子
The increment operator (++) increments its operand by 1. The increment operator can appear before or after its operand
遞增運算子 (++) 將運算元加 1。遞增運算子可以出現在運算元之前或之後
The first form is a prefix increment operation. The result of the operation is the value of the operand after it has been incremented.
第一種形式是首碼增量操作。該操作的結果是運算元加 1 之後的值。
The second form is a postfix increment operation. The result of the operation is the value of the operand before it has been incremented.
第二種形式是尾碼增量操作。該運算的結果是運算元增加之前的值。
以上是MSDN對++運算子的定義。此定義在C/C++、C#、JAVA (以下簡稱它們) 中的表現形式是一致的,故i++與++i在相應語言的編譯器中的表現也是一樣的。
標題“++ 運算子在C/C++、C#、JAVA中的差異”中的差異不是語言本身的差異,而是相應編譯器的差異。
剛剛說過,i++ 與 ++i 在它們的編譯器中表現是一樣的,但i=i++; 與i=++i;呢 i=++i 這個沒有分歧,有分歧的是i=i++ 在C/C++ 中
i=i++; i 自增前的值就是這個運算式的值,在運算式執行後 i自增1 如果 i=1 的話,i=i++ 就等於1 ,i=i++ 執行後 i=2。
int i=1; printf("%d\n",i = i++); printf("%d\n",i); 會列印出1和2 就是說會對i先賦值,然後對i自增1。
而在C#與JAVA中 i=i++ 等於 i 的值,而 i 的值會變化但仍然是之前的值。因為C#與JAVA的編譯器會嚴格遵循運算子優先順序,首先對=右側求值也就是 i 的值,
然後將 i 值入棧(將1入棧),之後優先順序的緣故 ++ 在 = 之前執行 i 先自增1 變成2,然後將棧裡的1 賦值給 i , i 這時又變回了1。
相對於C/C++的編譯器來說C#與JAVA的編譯器的做法更容易解釋也更合理,在我看產生的彙編代碼前,以為C/C++編譯器也會嚴格遵循優先順序,
故猜測是將 i 的地址入棧而不是直接將值入棧所以出現的不一致,但看過之後證明我的猜測是錯誤的,C/C++編譯器是將值放在寄存器中,
但與C#和JAVA的編譯器不同的是它未嚴格遵守運算子優先順序,就我目前掌握到的這屬於C/C++編譯器undefined的範疇,
它們的執行邏輯隨編譯器的不同而不同。所以千萬不要在C/C++程式中依賴求值順序,否則會死的很慘。
看下面一段代碼
int j, i=3;
j=(i+3)*++i;
printf("%d\n", i);
printf("%d\n", j);
在知道結果前,不妨先演算一下。結果是4,28 你算對了嗎? 而在C#與JAVA中則是4,24。
相應的彙編代碼
int j, i=3;
mov DWORD PTR _i$[ebp], 3 //將3儲存到_i$[ebp]中
j=(i+3)*++i;
mov eax, DWORD PTR _i$[ebp] // 將_i$[ebp]的值放寄存器eax
add eax, 1 // eax+1 結果放在eax中
mov DWORD PTR _i$[ebp], eax // eax的值放在_i$[ebp]中
mov ecx, DWORD PTR _i$[ebp] // 將_i$[ebp]的值放寄存器ecx
add ecx, 3 // ecx+3 結果放在ecx中
imul ecx, DWORD PTR _i$[ebp] // ecx*_i$[ebp]
mov DWORD PTR _j$[ebp], ecx // 將ecx的值放到_j$[ebp]中
產生的IL
[2] int32 j,[3] int32 i
int j, i=3;
IL_000e: ldc.i4.3 //將3入棧 棧中元素3
IL_000f: stloc.3 //對棧頂元素做彈出操作,並儲存到相應下標的變數中 棧中無元素
j=(i+3)*++i;
IL_0010: ldloc.3 //對相應下標的變數做取值操作,併入棧 棧中元素3
IL_0011: ldc.i4.3 //將3入棧 棧中元素3,3
IL_0012: add //對棧頂兩個元素依次做彈出並相加,結果入棧 棧中元素6
IL_0013: ldloc.3 //對相應下標的變數做取值操作,併入棧 棧中元素6,3
IL_0014: ldc.i4.1 //將1入棧 棧中元素6,3,1
IL_0015: add //對棧頂兩個元素依次做彈出並相加,結果入棧 棧中元素6,4
IL_0016: dup //對棧頂元素做複製操作併入棧 棧中元素6,4,4
IL_0017: stloc.3 //對棧頂元素做彈出操作,並儲存到相應下標的變數中 棧中元素6,4
IL_0018: mul //對棧頂兩個元素依次做彈出並相乘,結果入棧 棧中元素24
IL_0019: stloc.2 //對棧頂元素做彈出操作,並儲存到相應下標的變數中 棧中無元素
這裡沒有提供相關的JAVA位元組碼,但由於JVM和CLR都是 Stack-Based VM,所以只是具體指令不一樣,指令的運算邏輯與運算方式都是一樣的,可以參看IL部分。
我們將j=(i+3)*++i; 換成j=++i*(i+3);看看會發生什麼。相應的彙編代碼沒有任何改變,IL卻是大不相同,輸出的結果都是4,28
相關的IL
IL_000e: ldc.i4.3
IL_000f: stloc.3
IL_0010: ldloc.3
IL_0011: ldc.i4.1
IL_0012: add
IL_0013: dup
IL_0014: stloc.3
IL_0015: ldloc.3
IL_0016: ldc.i4.3
IL_0017: add
IL_0018: mul
IL_0019: stloc.2
相信聰明的你一定可以通過之前IL的註解演算出此段IL的運算過程,我就不贅述了。
最後是CSDN裡某同學的問題 “int index=1;count=10;執行index=(index++) % count;後輸出結果為何還是1”
後輸出結果為何還是1 指的是輸出index還是1
相關IL
[0] int32 index,[1] int32 count
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: ldc.i4.s 10
IL_0005: stloc.1
IL_0006: ldloc.0
IL_0007: dup
IL_0008: ldc.i4.1
IL_0009: add
IL_000a: stloc.0
IL_000b: ldloc.1
IL_000c: rem
IL_000d: stloc.0
相關彙編代碼
int index=1,count=10;
mov DWORD PTR _index$[ebp], 1
mov DWORD PTR _count$[ebp], 10 ; 0000000aH
index = (index++) % count;
mov eax, DWORD PTR _index$[ebp]
cdq
idiv DWORD PTR _count$[ebp]
mov DWORD PTR _index$[ebp], edx
mov eax, DWORD PTR _index$[ebp]
add eax, 1
mov DWORD PTR _index$[ebp], eax
通過上面的IL 你能否回答他的問題呢?