今天有個小學妹問我c語言中關於自增自減的問題,發現C語言的水真的深。
先看一段代碼吧。
#include <stdio.h> void main(){ int i=5,j=5,p,q; p=(i++)+(i++)+(i++); //1 q=(++j)+(++j)+(++j); //2 printf("%d,%d,%d,%d",p,q,i,j); }
首先先解釋p的值吧。
是不是很多在學習C語言的小伙伴覺得p應該等於18呢?
第一種:p = 15
如圖中所見,在vc環境下p的值為15,那么只可能有一種解釋:
p=5+5+5=15
在VC6.0中,第一個子表達式i++求完值后,其它子表達式中出現的變量i的值還沒有改變,依然是5。
表達式(i++) + (i++) + (i++)的值為15(5+5+5),求完值后,變量i會執行自增操作3次,其值會變成8。所以最終的結果為15和8。
**第二種:p = 18
在gcc和dev c++環境下p的值為18,不難理解:
p = 5+6+7=18
由於"i++"是先加后自增,所以是從5開始邊加邊自增,最終的結果為18和8。
嚴格意義上來說,這個表達式不“合法”。嚴格地說C語言程序中不能出現類似的表達式,它是非法的,雖然它能通過編譯系統的檢查並也能輸出一個結果。
再來解釋下q為什么是22(TC環境下是24(8+8+8))吧。
用VC中的debug反匯編:
q=(++j)+(++j)+(++j);
0040102F mov eax,dword ptr [ebp-4] //把j的值傳到寄存器EAX,dword ptr [ebp-4]存放的是變量j
00401032 add eax,1 //EAX加1
00401035 mov dword ptr [ebp-4],eax //EAX的值傳到變量j,這兩步實現第一個++j,此時j=6
00401038 mov ecx,dword ptr [ebp-4] //把j的值傳到寄存器ECX
0040103B add ecx,1 //ECX加1
0040103E mov dword ptr [ebp-4],ecx //ECX的值傳到變量j,這兩步實現第二個++j,此時j=7
00401041 mov edx,dword ptr [ebp-4] //把變量j傳到EDX
00401044 add edx,dword ptr [ebp-4] //再加上j的值,這一步實現了第一個加號的加法運算,內存中j變量的值仍是7
00401047 mov eax,dword ptr [ebp-4] //把j的值傳到寄存器EAX
0040104A add eax,1 //EAX加1
0040104D mov dword ptr [ebp-4],eax //把EAX的值傳送到變量j中,此時,變量j在內存中的值是8
00401050 add edx,dword ptr [ebp-4] //EDX加上j的值,EDX中存放的是前面的第一個加法運算的結果14,最后EDX的值為22
00401053 mov dword ptr [ebp-8],edx //把運算結果傳到內存中變量q的地方,所以最后變量q為22
可能有很多小白看不太懂上述的解釋 q=7+7+8=22 C語言的深水在這里就可以體現了,在C語言中,加法從左到右運算,先算前兩個加數的和,
再和第三個相加的過程中,他會先把前兩個括號內的東西執行完成后,再執行括號外的加法,也就是(7+7)+8=22。 如果是4個++j連續的話,
結果就應該是 7+7+9+9=32 。
親愛的你們懂了嗎?
ps: 在php語言中,測試上述代碼: ```
<?php $a = 5; $b = 5; $p = ($a++)+($a++)+($a++); $q = (++$b)+(++$b)+(++$b); echo $p; echo $q; echo $a; echo $b;
得到的18和8我就不多說了,21也是比較符合我們正常思路的6+7+8。
而在python,go,以及ruby中,沒有自增運算符,這樣也就大大減少了這種因版本和編譯器的差異導致運行結果不同的概率。