首頁  >  問答  >  主體

c++ - C语言 关于b=a+++a++运算问题?

篇幅有点长,让您受累了。。。

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++运算的猜测步骤为:

  1. 第一个a++ //此时a=10

  2. 第二个a++ //因为第一步运算完后a自增1,所以此时a=11,是第一个a++运算后的值

  3. 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啊?

阿神阿神2764 天前2778

全部回覆(17)我來回復

  • PHPz

    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、處理+(和)運算:因為+屬於二目操作運算(也就是需要兩個操作數的運算),先從堆疊裡面彈出兩個操作數,即剛入棧的1011,再做+運算,即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,而且不同優化等級都試了,如果屬實那麼可能是其他選項或版本間問題。

    最後,編譯原理我也沒有深入學習過,只能說個大概,也不是所有的編譯器都是採用堆疊方式來求解表達式,有錯誤的地方歡迎斧正。
    還是那句話,盡量寫人容易理解的程式碼。

    回覆
    0
  • 阿神

    阿神2017-04-17 14:02:29

    你拿不同的編譯器去測試,會發現結果跟你現在的結果又不一樣。或者你在同一個編譯器下,改下編譯參數,結果又不一樣。歸根究底這類問題是undefined behavior。所以,你要記得的不是i到底什麼時候自加,而是保證自己不去寫這種垃圾程式碼。 。

    回覆
    0
  • PHP中文网

    PHP中文网2017-04-17 14:02:29

    題主可以看一下這個
    [] (https://en.m.wikipedia.org/wiki/Undefined_behavior)
    在兩個序列點(sequence point)之間對同一個物件進行兩次修改為未定義行為,不可預測

    回覆
    0
  • 天蓬老师

    天蓬老师2017-04-17 14:02:29

    standard C沒有定義
    編譯器依照自己的規則自己處理。

    回覆
    0
  • 大家讲道理

    大家讲道理2017-04-17 14:02:29

    這個問題裘宗燕老師早已經給了完整的答案。

    回到前面的例子:「誰知道下面C語句給n什麼值?」

    m = 1; n = m++ +m++;

    正確答案是:不知道!語言沒有規定它應該算出什麼,結果完全依賴具體係統在具體上下文中的具體處理。其中牽涉到運算物件的求值順序和變數修改的實作時刻問題。

    http://www.math.pku.edu.cn/teachers/qiuzy/technotes/expression2009.pdf

    回覆
    0
  • 阿神

    阿神2017-04-17 14:02:29

    未定義行為。

    編譯器實作不同是因為++是產生影響的,編譯器消除影響的時間點不同。

    回覆
    0
  • 天蓬老师

    天蓬老师2017-04-17 14:02:29

    這種問題沒什麼意義,你的程式碼中根本不能出現這種語句

    回覆
    0
  • 迷茫

    迷茫2017-04-17 14:02:29

    我想起我以前看過的一篇文章,和你的問題有些類型.
    連結

    不管怎麼樣,都最好在專案中盡量使用簡潔易懂的語句,不要出現這種不知道會出什麼奇怪的問題的程式碼.

    回覆
    0
  • 怪我咯

    怪我咯2017-04-17 14:02:29

    首先我很佩服題主的探索精神。
    但我建議你去看:

    1. C語言運算子的優先權

    2. C語言運算子的結合性

    好像還不夠,那再去看一下C語言規範中相關運算符的詳細語意描述:

    http://eli-project.sourceforge.net/c_html/c.html

    好像還不行,那隻能去看編譯器的實作了。

    回覆
    0
  • PHPz

    PHPz2017-04-17 14:02:29

    這個好像跟編譯器是有關係的,其實理解時候你可以這樣理解i++ => b = i; i = i + 1;然後回傳值I++的回傳值是b,這樣理解就行了。有的編譯器是返回i,然後 i = i + 1;

    回覆
    0
  • 取消回覆