C++的求值順序問題?

primer上描述的比較簡單,看了下網上的博客還有cppreference,發現有個不一致的地方,reference上舉例如下:

i = i++ + 1; // undefined behavior (but i = ++i + 1; is well-defined)

為何後一種就well-defined了呢,和很多博客里都不一樣啊


是這個頁面里的吧:Order of evaluation

其實前面就有講到:

Evaluations

There are two kinds of evaluations performed by the compiler for each expression or subexpression (both of which are optional):

  • value computation: calculation of the value that is
    returned by the expression. This may involve determination of the
    identity of the object (glvalue evaluation, e.g. if the expression
    returns a reference to some object) or reading the value previously
    assigned to an object (prvalue evaluation, e.g. if the expression
    returns a number, or some other value)
  • side effect: access (read or write) to an object
    designated by a volatile glvalue, modification (writing) to an object,
    calling a library I/O function, or calling a function that does any of
    those operations.

還有:

Rules

4) The value computation of the built-in post-increment and post-decrement operators is sequenced before its side-effect.

5) The side effect of the built-in pre-increment and pre-decrement operators is sequenced before its value computation (implicit rule due to definition as compound assignment)

對未定義行為的說明:

1) If a side effect on a scalar object is unsequenced relative to another side effect on the same scalar object, the behavior is undefined.

------------------------------------------------------------------------------------------------------------------------------------------

這裡的value computation就是計算表達式的值,side effect就是對對象進行讀寫。

在這個例子里:

i = ++i + 1; //前置++

根據前述第5條規定,++i的side effect(即改變i的值)先於計算++i + 1這個表達式的值執行,兩者都執行後再用這個表達式的值為i賦值(賦值運算符=的side effect)。所以在這一句里的處理順序是這樣的:

  1. 通過++i改變i的值
  2. 計算++i + 1的結果
  3. 把第2條里計算出來的結果賦給i

在這種情況下,兩個side effect(++和=)的順序是明確的(一個在value computation前,一個在後),不會有undefined behavior。

------------------------------------------------------------------------------------------------------------------------------------------

然而使用後置++就不一樣了:

i = i++ + 1; //後置++

根據前述第4條規定,i++的side effect(同樣為改變i的值)會在計算i++ + 1這個表達式的值之後執行。於是:

  1. 計算i++ + 1的結果
  2. 執行哪個side effect?

在原頁面的Rules板塊並沒有規定應該是先執行i++還是給i賦值,這種情況滿足未定義行為的第一條說明(兩個side effect沒有規定先後順序的情況)。所以這條表達式是未定義行為,編譯器可以選擇先執行i++或者先為i賦值,這樣在這條語句結束後i的值是無法確定的:

int i = 0; //i的初始值為0
i = i++ + 1; //未定義行為

//如果先執行i++,則以上語句等同於:
int i = 0;
int temp = i + 1; //後置++返回i的原始值,temp的值為1
i++; //i的值變為1
i = temp; //i的值依然為1

//如果先為i賦值,則以上語句等同於:
int i = 0;
int temp = i + 1; //同上
i = temp; //i的值變為1
i++; //i的值變為2

//i的值可能為1或2,所以i = i++ + 1;屬於未定義行為


寫出這樣代碼的人,我會直接把代碼呼他臉上。


如果你不想當語言律師的話,最好認為後一種也不是 well-defined,還是 undefined。


要解釋也不是不行,只不過超級費事,因為C++11重新整理了有關sequence的說明,去掉了sequence point。。

先解釋一下為什麼i = i++ + 1是UB。

5.2.6

1 The value computation of the ++ expression is sequenced before the modification of the operand object.

5.18

1 the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.

1.9

15 The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.

A is sequenced before B 記作 A ? B。

由上文可知,i++的求值 ? i的值的更改1

i++的求值 ? i++ + 1的求值 ? 賦值,即i的值更改2

所以既不是更改1 ? 更改2,也不是反過來,稱作更改1和更改2是unsequenced。

但是

1.9

15 If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, and they are not potentially concurrent (1.10), the behavior is undefined.

得到UB。

相反

5.3.2

1 If x is not of type bool, the expression ++x is equivalent to x+=1

5.18

7 The behavior of an expression of the form E1 op = E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.

++i 等價於 i = i + 1。

所以 i的值更改1 ? ++i的求值(參見5.18引文後半句) ? ++i + 1的求值 ? i的值更改2

所以不是UB。

差不多就這樣。。大概有錯吧。不過也沒什麼卵用。。


CSDN的C/C++論壇的置頂帖就是你要的


推薦閱讀:

新手學習輪滑,入手使用一年的輪滑鞋可以嘛?
對於知乎新人你們有什麼好的建議?

TAG:編程 | 計算機 | 新手 | C |