<delect id="sj01t"></delect>
  1. <em id="sj01t"><label id="sj01t"></label></em>
  2. <div id="sj01t"></div>
    1. <em id="sj01t"></em>

            <div id="sj01t"></div>
            C語言

            C/C++ 表達式求值順序

            時間:2025-06-01 11:55:58 C語言 我要投稿
            • 相關推薦

            關于C/C++ 表達式求值順序

              導語:表達式求值順序不同于運算結合性和優先級。下面是一個經典例子,被 ISO C99/ C++98 /03 三大標準明確提到:他的結果是不確定(unspecified) 的。 下面是關于C/C++ 表達式求值順序,歡迎學習:

              i = ++i + 1; // The behavior is unspecified

              在介紹概念之前,我們先解釋一下它的結果。這個表達式( expression )包含3個子表達式( subexpression ):

              e1 = ++i

              e2 = e1 + 1

              i = e2

              這三個子表達式都沒有順序點( sequence point ),而 ++ i 和 i = e3 都是有副作用( side effect )的表達式。由于沒有順序點,語言不保證這兩個副作用的順序。

              更加可怕的是,如果i 是一個內建類型,并在下一個順序點之前被改寫超過一次,那么結果是未定義(undefined)的!比如本例中如果有:

              int i = 0x1000fffe;

              i = ++i + 1; // The result is undefined!!

              你也許會認為他的結果是加1 或者加2,其實更糟糕 —— 結果可能是 0x1001ffff 。他的高字節接受了一個副作用的內容,而低字節則接受了另一個副作用的內容! 如果i 是指針,那么將很容易造成程序崩潰。

              為什么要這么做呢?因為對于編譯器提供商來說,未確定的順序對優化有相當重要的作用。比如,一個常見的優化策略是“減少寄存器占用和臨時對象”。編譯器可以重新組織表達式的求值,以便盡量不使用額外的寄存器以及臨時變量。 更加嚴格的說,即使是編譯器提供商也無法完全徹底序列化指令(比如無法嚴格規定讀和寫的順序),因為CPU本身有權利修改指令順序,以便達到更高的速度。

              下面的術語以 ISO C99 和 C++03為準。譯名為參考并附帶原術語對照,如有解釋不當或者錯誤望指正。

              表達式有兩種功能。每個表達式都產生一個值( value ),同時可能包含副作用( side effect ),比如:他可能修改某些值。

              規則的核心在于 順序點( sequence point ) [ C99 6.5 Expressions 條款2 ] [ C++03 5 Expressions 概述 條款4 ]。 這是一個結算點,語言要求這一側的求值和副作用(除了臨時對象的銷毀以外)全部完成,才能進入下面的部分。 C/C++中大部分表達式都沒有順序點,只有下面五種表達式有:

              1 函數。函數調用之前有一個求值順序點。

              2 && || 和 ?: 這三個包含邏輯的表達式。其左側邏輯完成后有一個求值順序點。

              3 逗號表達式。逗號左側有一個求值順序點。

              注意,他們都只有一個求值順序點,2和3的右側運算結束后并沒有求值順序點。

              在兩個順序點之間,子表達式求值和副作用的順序是不確定的。假如代碼的結果與求值和副作用發生順序相關,我們稱這樣的代碼有不確定的行為(unspecified behavior)。 而且,假如期間對一個內建類型執行一次以上的寫操作,則是未定義行為(undefined behavior)——我們知道,未定義行為帶來最好的后果是讓你的程序立即崩掉。

              n = n++; // 兩個副作用,對于內建對象產生是未定義行為

              幾乎所有表達式,求值順序都不確定。比如,下面的加法, f1 f2 f3的調用順序是任意的:

              n = f1() + f2() + f3(); // f1 f2 f3 調用順序任意

              而函數也只在實際調用前有一個求值順序點。所以,常見于早期 C 語言教材的這類題目,是錯題:

              printf("%d",--a+b,--b+a); // --a + b 和 --b + a 這兩個子表達式,求值順序不確定

              天啊,甚至可能出現未定義行為?那么堅決不寫與實現相關的代碼是最好的對策。即使是不確定行為(比如函數調用時) 只要沒有順序點編譯器怎么做方便就怎么做。 有些人認為函數調用參數求值與入棧順序相關,這是一種誤導。這個東西要解釋,無異于事后諸葛亮:

              void f( int i1, int i2, int i3, int i4 ){

              cout<< i1 << ' ' << i2 << ' ' << i3 << ' ' << i4 << endl;}

              int main(){

              int i = 0;

              f( i++, i++, i++, i++ );}

              這個有四個表達式求值,同時每個表達式都有負作用。這八個操作順序是任意的,那么結果如何?未定義。

              請用 VC7.1 Debug和 Release 分別測試這同一份代碼,結果是不同的:

              0 0 0 0 [release]

              3 2 1 0 [debug]

              事實上,鑒于前面的討論,如果換一些其他初始值,這里甚至會出現錯位而得到千奇百怪的詭異結果。

              再看看C/C++標準中的其他經典例子:

              [C99] 6.5.2.2 Function call

              條款12 EXAMPLE 在下面的函數調用中:

              (*pf[f1()]) ( f2(), f3() + f4() )

              函數 f1 f2 f3 和f4 可能以任何順序被調用。 但是,所有副作用都必須在那個 pf[ f1() ] 返回的函數指針產生的調用前完成。

              [C++03] 5 Expressions 概論4

              i = v[i++]; // the behavior is unspecified

              i = 7, i++, i++; // i becomes 9 ( 譯注: 賦值表達式比逗號表達式優先級高 )

              i = ++i + 1; // the behavior is unspecified

              i = i + 1; // the value of i is incremented

              More Effective C++ 告誡我們, 千萬不要重載 &&, || 和, 操作符[ MEC ,條款7 ]。為什么?

              以逗號操作符為例,每個逗號左側有一個求值順序點。假如ar是一個普通的對象,下面的做法是無歧義的:

              ar[ i ], ++i ;

              但是,如果ar[ i ] 返回一個 class A 對象或引用,而它重載了 operator, 那么結果不妙了。那么,上面的語句實際上是一個函數調用:

              ar[ i ].operator, ( ++i );

              C/C++ 中,函數只在調用前有一個求值順序點。所以 ar[i] 和 ++i 的求值、以及 ++i 副作用的順序是任意的。這會引起混亂。

              更可怕的是,重載 && 和 || 。 大家已經習慣了其速死算法: 如果左側求值已經決定了最終結果,則右側不會被求值。而且大家很依賴這個行為,比如是C風格字符串拷貝常常這樣寫:

              while( p && *p )

              *pd++ = *p++;

              假如p 為 0, 那么 *p 的行為是未定義的,可能令程序崩潰。 而 && 的求值順序避免了這一點。 但是,如果我們重載 && 就等于下面的做法:

              exp1 .operator && ( exp2 )

              現在不僅僅是求值混亂了。無論exp1是什么結果,exp2 必然會被求值。


            【C/C++ 表達式求值順序】相關文章:

            C/C++ 語言中的表達式求值04-30

            C/C++內存管理05-05

            C和C++的關系解說01-17

            C/C++函數調用的方式07-29

            C語言和C++的分別06-18

            C語言和C++的區別04-01

            C語言和C++的區別精選02-28

            C++調用C函數的方法05-21

            C++ this指針詳解07-04

            <delect id="sj01t"></delect>
            1. <em id="sj01t"><label id="sj01t"></label></em>
            2. <div id="sj01t"></div>
              1. <em id="sj01t"></em>

                      <div id="sj01t"></div>
                      黄色视频在线观看