[英]Assignment operator sequencing in C11 expressions
C11標准(ISO / IEC 9899:2011)在表達式中引入了新的副作用測序定義( 參見相關問題 )。 序列點概念已經在關系之前進行了序列補充,並且在關系之后 進行了 排序,這些關系現在是所有定義的基礎。
第6.5節“表達式”,第2點說:
如果相對於對同一標量對象的不同副作用或使用相同標量對象的值進行值計算,對標量對象的副作用未被排序,則行為未定義。 如果表達式的子表達式有多個允許的排序,則如果在任何排序中發生這種未測序的副作用,則行為是不確定的。
稍后,第6.5.16節“分配操作員”,第3點指出:
在左右操作數的值計算之后,對更新左操作數的存儲值的副作用進行排序。 對操作數的評估是不確定的。
第一個引用的段落(6.5 / 2)由兩個例子支持(與C99標准相同):
a[i++] = i; //! undefined
a[i] = i; // allowed
這可以通過以下定義輕松解釋:
因此, i++
(LHS)的副作用與i
(RHS)無關,它給出了未定義的行為。
i = ++i + 1; //! undefined
i = i + 1; // allowed
但是,此代碼似乎在兩種情況下都會導致定義的行為:
因此, ++i + 1
的執行應先於更新i
的副作用,這意味着相對於對同一標量對象的不同副作用或使用的值計算,對未標測的標量對象沒有副作用相同標量對象的值。
使用C99標准提出的術語和定義很容易解釋這些例子( 參見相關問題 )。 但根據C11的術語,為什么i = ++i + 1
未定義?
更新
我在這里改變我的答案,雖然它是在C ++ 11中,但在C11中沒有很好地定義。 這里的關鍵是++i
的結果不是左值,因此在評估++i
之后不需要進行左值到右值的轉換,因此我們無法保證將讀取++i
的結果然后。 這與C ++不同,因此我最初鏈接的缺陷報告取決於這個關鍵事實:
[...]左值表達式++ i然后對結果進行左值到右值的轉換。 保證在計算加法運算之前對增量副作用進行排序[...]
我們可以通過轉到C11草案標准部分6.5.3.1
前綴增量和減量運算符來看到這一點:
[...]表達式++ E相當於(E + = 1)。[...]
然后是6.5.16
分配操作員 ( 強調我的前進 ):
賦值運算符將值存儲在左操作數指定的對象中。 賦值表達式在賦值后具有左操作數的值, 111 但不是左值 。[...]
和腳注111
說:
允許實現讀取對象以確定值,但不需要,即使對象具有volatile限定類型。
即使它是易失性的,也不需要讀取對象來確定它的值。
原始答案
據我所知,這實際上是定義良好的,這個例子已從使用類似語言的C ++草案標准中刪除。 我們可以在637看到這一點。 排序規則和示例不同意說:
以下表達式仍作為未定義行為的示例列出:
i = ++i + 1;
但是,似乎新的排序規則使這個表達式定義明確:
並且解決方案是打擊前綴示例並使用后綴示例,而這顯然是未定義的:
更改1.9 [intro.execution]第16段中的示例,如下所示:
i =
++ ii ++ + 1; //行為未定義
在您正確引用時,標准規定了分配(6.5.16)
在左右操作數的值計算之后,對更新左操作數的存儲值的副作用進行排序。
(增量運算符沒有區別,它只是偽裝的賦值)
這意味着有兩個值計算(左和右),然后在這些值之后對賦值的副作用進行排序。 但它僅針對價值計算進行排序,而不是針對這些可能產生的副作用。 所以最后我們面臨着兩個副作用( =
運算符和++
運算符),它們彼此之間沒有順序。
但根據C11的術語,為什么
i = ++i + 1
未定義?
C11說,在留下的副作用, i
被測序但不是左邊和右邊的值計算(評估) i
。
很明顯,LHS的副作用將在評估LHS和RHS的表達后發生。
為了解釋這個,可以有一個更好的例子
int i = 1;
i = i++ + 3;
(首先讓我們假設這個例子不會調用UB)。 現在i
的最終值可能是4
或2
。
案例1 。
左邊的i
被取出然后它被遞增並且3
被添加到它,最后4
被分配給i
。
案例2 。
左邊是i
,然后將3
添加到它,然后將4
分配給i
,最后i
遞增。 在這種情況下, i
的最終值為2
。
盡管對左i
的副作用是有序的,但是沒有定義存儲到i
的最終值,即它不一定是由賦值,因此對i
的副作用是未定序的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.