篇幅有点长,让您受累了。。。
a++这个问题一直很困扰,自己做了个测试,虽然都知道a++是先使用
a再进行自加,疑问点就是这个a使用的期限是什么时候结束?一开始以为是表达式代码行完成后,在第二行代码需要试用a时,就是自加结果,这也是最常用的情况,也很好理解,如:
int a=0,b;
b=a++;
printf("a=%d b=%d",a,b);//输出:a=1,b=0
那假如在同一行表达式中出现2次a++,也就是说a++后又与其他变量进行运算,如下代码:
int a=10,b=0;
b=a+++a++;
printf("a=%d b=%d",a,b);//输出a=12 b=21
如果上面的理论成立那b应该等于20,根据执行结果显然不成立,所以我觉得应该是当执行a++运算时,a还是10,但当a在与其他变量继续运算时a就已经完成了自增,就是自增后的值与其他变量运算了,在变下代码再测试:
int a=10,b=0;
b=a+++b;//b=10(因为a++优先级大于++b,所以直观点应该是b=(a++)+b,尽管此时括号是多余的)
显然这种说法也不成立。
对b=a+++a++运算的猜测步骤为:
第一个a++ //此时a=10
第二个a++ //因为第一步运算完后a自增1,所以此时a=11,是第一个a++运算后的值
b=a+a //b=11+11=22,这点就不理解了,之所以最终结果这个b=21,难道是b=10+11吗,但中间+号的表达式两端都是a,应该两端的值都是一样的啊,应是22或20啊,怎么会是21,b=(a++)+(++a) 这个结果为22,应该可以说明+号两边都是a的话,第一个表达式a++中a会被++a后的值覆盖,所以b=11+11。
求解释b=a++a++的详细运算步骤,为啥会是b=21?
还有个问题:
int i=1;
int j=0;
for(;j<5;j++){
i=i++;
printf("i=%d",i);
}
printf("i=%d",i);
为什么i=1?为什么i=i++执行完后,在执行j<5之前或在下一轮执行前i没有自加?及时for循环中i=1,那for循环执行完后i应该至少会加1吧,起码i也得等于2啊?
PHPz2017-04-17 14:02:29
首先,其實樓上已經說的很明確了,寫出這樣的程式碼是很不負責任的行為。不過既然要探討,那就以探討的方式來討論這個問題吧。我測試的程式碼不複雜就下面這幾行:
#include <stdio.h>
int main()
{
int a=0,b=0;
a = 10;
b = a+++a++;
printf("%d %d\n", b, a);
return 0;
}
編譯器:gcc version 4.9.2 (Raspbian 4.9.2-10)
輸出的結果:
21 12
我們先討論a+++a++的等價形勢,根據C語言運算符優先級,後置++的優先級高於+的優先權,也就是a+++a++原則上可以等價於(a++)+(a++)。
那麼,編譯器是怎麼理解的呢?它把它理解成文法樹,類似這樣:
+
/ \
/ \
a++ a++
之後,編譯器又怎麼處理呢,基於堆疊操作的一般是這樣的:
1、處理第一個a++運算:因為是後置運算,先把a(此時a=10)入棧了,然後a=a+1,現在的a已經是11了;
2、處理第二個a++運算:一樣的,先把a(此時a=11)入棧了,然後a= a+1,現在的a已經是12了;
3、處理+(和)運算:因為+屬於二目操作運算(也就是需要兩個操作數的運算),先從堆疊裡面彈出兩個操作數,即剛入棧的10和11,再做+運算,即10+11,最後的結果就是21。
基本描述就是:
push(a), a入棧(10)
a=a+1, a加1
push(a), a入棧(11)
a=a+1, a加1
b=pop()+pop(), 出棧兩個操作數(10,11)相加,賦值到b(=21)
在基於棧的處理方式中,操作數都是從棧彈出來再進行運算的(當然,棧裡面的數據也是之前壓進去的),而不是直接拿a去計算的,現代編譯器基本上都是基於棧方式實現的,我猜測你的疑惑估計就在這一點上了。如果你想了解的話可以去看看編譯原理方面的內容。
依照編譯器的一般做法應該就是上述步驟了,啟用優化之後可能有些差異,看到有人說(a++)+(a++)在gcc4.5.1最後得到20,我手上沒有4.5.1,但是在4.9.2和4.2.1上面都是得到21,而且不同優化等級都試了,如果屬實那麼可能是其他選項或版本間問題。
最後,編譯原理我也沒有深入學習過,只能說個大概,也不是所有的編譯器都是採用堆疊方式來求解表達式,有錯誤的地方歡迎斧正。
還是那句話,盡量寫人容易理解的程式碼。
阿神2017-04-17 14:02:29
你拿不同的編譯器去測試,會發現結果跟你現在的結果又不一樣。或者你在同一個編譯器下,改下編譯參數,結果又不一樣。歸根究底這類問題是undefined behavior。所以,你要記得的不是i到底什麼時候自加,而是保證自己不去寫這種垃圾程式碼。 。
PHP中文网2017-04-17 14:02:29
題主可以看一下這個
[] (https://en.m.wikipedia.org/wiki/Undefined_behavior)
在兩個序列點(sequence point)之間對同一個物件進行兩次修改為未定義行為,不可預測
大家讲道理2017-04-17 14:02:29
這個問題裘宗燕老師早已經給了完整的答案。
回到前面的例子:「誰知道下面C語句給n什麼值?」
m = 1; n = m++ +m++;
正確答案是:不知道!語言沒有規定它應該算出什麼,結果完全依賴具體係統在具體上下文中的具體處理。其中牽涉到運算物件的求值順序和變數修改的實作時刻問題。
http://www.math.pku.edu.cn/teachers/qiuzy/technotes/expression2009.pdf
怪我咯2017-04-17 14:02:29
首先我很佩服題主的探索精神。
但我建議你去看:
C語言運算子的優先權
C語言運算子的結合性
好像還不夠,那再去看一下C語言規範中相關運算符的詳細語意描述:
http://eli-project.sourceforge.net/c_html/c.html
好像還不行,那隻能去看編譯器的實作了。
PHPz2017-04-17 14:02:29
這個好像跟編譯器是有關係的,其實理解時候你可以這樣理解i++ => b = i; i = i + 1;然後回傳值I++的回傳值是b,這樣理解就行了。有的編譯器是返回i,然後 i = i + 1;