Summary
1)++和--參與混合運算結果是不確定的
,如r = (i++) + (i++);等
- C++只規定了++和--對應指令的
相對執行次序
(取值和自增的
相對順序) - ++和--
對應的彙編指令不一定連續執行
- 在混合運算中,++和--的
彙編指令可能被打斷執行
(取值和自增可能被打斷了,中間插入了其他程式碼)
2)編譯器的貪心法
- 編譯器以
從左向右
的順序,一個一個地儘可能多的讀入字元
- 當讀入的字元
不可能
和已經讀入的字元組成合法符號為止
3)空格
可以作為C語言中一個完整符號的休止符
,編譯器讀入空格後會立即對之前讀入的符號進行處理
。
++和--操作符剖析
1、Demo1
以下程式碼的輸出是?
int i = 0; int r = 0; r = (i++) + (i++) + (i++); printf("i = %d\n, r = %d\n", i, r); r = (++i) + (++i) + (++i); printf("i = %d\n, r = %d\n", i, r);
程式碼初步分析:
第一行輸出:i = 3, r = 3; // 按照從左向右的計算方法,i先取值,再自增, // 則r = 0 + 1 + 2 = 3; 第二行輸出:i = 6, r = 16; // 同上,r = 4 + 5 + 6 = 15;
以上程式碼在VS2015編譯器的實際輸出結果為:
i = 3, r = 0;
i = 6, r = 18;
藉助Vs編譯器檢視反彙編結果:
程式碼分析:
對於r = (i++) + (i++) + (i++); // 先取了i的值做了相加,賦值給r;然後i自增3次
在彙編層面,做的操作依次是:
1)00C542BC mov 將i代表的這個地址中的值放到暫存器eax中,為0
2)00C542BF add eax的值和i的值累加,累加和仍然為0
3)00C542C5 mov 將eax裡的值放到r變數代表的記憶體裡,
所以r的值為0
4)後面做了3次相同的操作:將i變數記憶體裡的值放到ecx暫存器裡,然後加1,
再把ecx裡的值放回i變數的記憶體裡;重複2次
所以i的值為3
對於r = (i++) + (i++) + (i++); // 先給i自增了3次;然後把i的值加3次給了r
在彙編層面,做的操作依次是:
1)009A42F8 到 009A42FE 將i代表的這個地址中的值放到暫存器eax中;
然後eax裡的值自增1;
寫回i的記憶體裡,此時i的值為4
重複2次後,i的值為6
所以i的值為6
2)009A4313 mov 把i裡的值移動到eax暫存器中,eax裡的值為6
3)009A4316 add eax裡的值加上i的值,即6+6,eax裡的值為12
4)009A4319 add eax裡的值加上i的值,即12+6,eax裡的值為18
5)009A4319 mov eax裡的值寫回r代表的記憶體裡
所以r的值為18
這一段程式碼,我們的分析和實際的編譯器結果大不相同
;那麼其他編譯器又如何?bcc編譯器和vc編譯器的結果一致
,而gcc編譯器的結果則是“r = 0 和 r = 16”
以上程式碼在java編譯器中的輸出結果:
// test.java
public static void main(String[] args) {
System.out.println("test.java file name must be equal to the class name test");
int i = 0;
int r = 0;
r = (i++) + (i++) + (i++);
System.out.println("i = " + i + ", r = " + r);
r = (++i) + (++i) + (++i);
System.out.println("i = " + i + ", r = " + r);
}
// 編譯:javac test.java
// 執行:java test
// 輸出:i = 3, r = 3
i = 6, r = 15 和分析的一致
以上的測試說明:++和--參與混合運算結果是不確定的
- C++只規定了++和--對應指令的相對執行次序
- ++和--對應的彙編指令不一定連續執行
- 在混合運算中,++和--的彙編指令可能被打斷執行
2、Demo2
以下程式碼的輸出是?
int i = 0; int j = ++i+++i+++i; int a = 1; int b = 4; int c = a+++b; int* p = &a; b = b/*p; printf("i = %d\n", i); printf("j = %d\n", j); printf("a = %d\n", a); printf("b = %d\n", b); printf("c = %d\n", c);
編譯器的貪心法
- 編譯器以
從左向右
的順序,一個一個地儘可能多的讀入字元
- 當讀入的字元
不可能
和已經讀入的字元組成合法符號為止
程式碼分析:
int j = ++i+++i+++i;
// 編譯器先讀到1個'+',不知道啥意思;
// 繼續讀到1個'+',它覺得這是個前置的'++'
// 然後讀到了i,確定了這是個'++i'表示式
// 繼續讀到1個'+',這可能是一個加法的運算子 '+'
// 繼續讀到1個'+',這時候判斷是一個後置的 '++',後面再讀任何數都不對了,讀到變數,不合法;讀到符號,也不對;
// 這時候編譯器就停止處理了,所以編譯器就得到了 “++i++”
// 然後計算得到了 “1++”,對一個右值1進行自增,自然會編譯錯誤!
int c = a+++b;
// 編譯器依次讀了3個字元'a++',知道這是個後置的++
// 然後讀到了1個'+',覺得這個可能是個加法運算子
// 繼續讀到了b,後面也沒有其他字元了,所以得到了 (a++) + b
// 計算得到了c = 5
b = b/*p; // error,編譯器會將/*識別為註釋,因此整個程式會編譯失敗
// 如果願意是用b的值除以*p的值,那麼就應該用括號或者空格表明
b = b / (*p); 或 b = b / *p;
本文總結自“狄泰軟體學院”唐佐林老師《C語言進階課程》。
如有錯漏之處,懇請指正。