[英]Operator associativity, precedence
我只是想知道,對於以下代碼,編譯器是否單獨使用關聯性/優先級或其他一些邏輯來評估。
int i = 0, k = 0;
i = k++;
如果我們根據關聯性和優先級進行評估,則postfix ++
的優先級高於=
,因此k++
(變為1
)首先被評估,然后是=
,現在 k 的值為1
被分配給i
。
所以i
和k
的值是1
。 但是, i is 0
, k is 1
。
所以我認為編譯器拆分了這個i = k++;
一分為二(i = k; k++;)
。 所以這里的編譯器不是為了語句關聯性/優先級,它也分割了行。 有人可以解釋編譯器如何解析這些類型的語句嗎?
++
做兩件不同的事情。 k++
做了兩件事:
k
。k
。這些是分開的:
k
的值是i = k++;
.k
是一個副作用。 它不是主要評估的一部分。 程序可以在計算表達式的 rest 之后或在此期間增加k
的值。 它甚至可以增加表達式的 rest 之前的值,只要它“記住”用於表達式的預增量值。 這實際上與優先級或關聯性無關。 ++
運算符的增量部分始終與表達式的主求值分開。 無論存在什么其他運算符,用於k++
的值始終是增量之前的k
值。
重要的是要了解++
的增量部分與主要評估分離並且在時間上有點“浮動” - 它沒有錨定到代碼中的某個位置,並且您無法控制它何時發生。 這很重要,因為如果操作數有其他用途或修改,例如在k * k++
中,則增量可能發生在其他事件的主要評估之前、期間或之后。 發生這種情況時,C 標准沒有定義程序的行為。
后綴運算符的優先級高於賦值運算符。
此表達式帶有賦值運算符
i = k++
包含兩個操作數。
它等效地可以重寫為
i = ( k++ );
表達式k++
的值為0
。 所以變量i
將得到值0
。
賦值運算符的操作數可以按任何順序求值。
根據 C 標准(6.5.2.4 后綴遞增和遞減運算符)
2 后綴 ++ 運算符的結果是操作數的值。 作為副作用,操作數 object 的值會遞增(即,相應類型的值 1 會添加到其中)。
和(6.5.16 賦值運算符)
3 賦值運算符將值存儲在左操作數指定的 object 中。 賦值表達式在賦值后具有左操作數的值,111) 但不是左值。 賦值表達式的類型是左操作數在左值轉換后的類型。 更新左操作數的存儲值的副作用是在左操作數和右操作數的值計算之后排序的。 操作數的評估是無序的。
它類似於(僅用於說明的附加序列點):
i = k; // i = 0
k = k + 1; // k = 1
與 C++ 不同,C 沒有“通過引用”。 僅“按值傳遞”。 我打算借一些C++來解釋一下。 讓我們將后綴和前綴的 ++ 功能實現為常規函數:
// Same as ++x
int inc_prefix(int &x) { // & is for pass by reference
x += 1;
return x;
}
// Same as x++
int inc_postfix(int &x) {
int tmp = x;
x += 1;
return tmp;
}
所以你的代碼現在相當於:
i = inc_postfix(k);
編輯:
對於更復雜的事情,它並不完全等價。 例如,Function 調用引入了序列點。 但以上足以解釋 OP 會發生什么。
運算符關聯性不適用於此處。 運算符優先級僅說明哪個操作數堅持哪個運算符。 在這種情況下它並不是特別相關,它只是說表達式應該被解析為i = (k++);
而不是(i = k)++;
這沒有任何意義。
從那時起,如何評估/執行此表達式由每個運算符的特定規則指定。 后綴運算符被指定為 (6.5.2.4):
結果的值計算在更新操作數的存儲值的副作用之前排序。
也就是說, k++
保證評估為0
,然后在稍后的某個時間點, k
增加 1。我們真的不知道什么時候,只知道它發生在k++
評估點之間但在下一個序列點之前, 在這種情況下;
在行尾。
賦值運算符的行為類似於 (6.5.16):
更新左操作數的存儲值的副作用是在左操作數和右操作數的值計算之后排序的。
在這種情況下, =
的右操作數在更新左操作數之前計算其值。
實際上,這意味着可執行文件可以如下所示:
k
被評估為 0i
設置為 0k
增加 1或這個:
k
被評估為 0k
增加 1i
設置為 0這里的根本問題是優先級不是思考什么的正確方法
i = k=+;
方法。
讓我們談談k++
的真正含義。 k++
的定義是,如果給您k
的舊值,然后將k
的存儲值加 1。 (或者,換一種說法,它取k
的舊值加上 1,並將其存儲回k
,同時給你k
的舊值。)
就表達式的rest而言,重要的是k++
的值是多少。 所以當你說
i = k++;
“什么存儲在i
中?”問題的答案是,“ k
的舊值”。
當我們回答“什么存儲在i
中?”的問題時,我們根本不會考慮優先級。 我們思考后綴++
運算符的含義。
另請參閱這個較舊的問題。
后記:另一件你必須非常小心的事情是當你考慮一個附帶問題時,“它什么時候將新值存儲到k
中?事實證明這是一個很難回答的問題,因為答案不是很好定義為你可能喜歡的。新值在它所在的較大表達式結束之前的某個時間被存儲回k
中(正式地,“在下一個序列點之前”),但我們不知道它是發生在之前還是之后,比如說,事物被存儲到i
中的點,或者在表達式中其他有趣點之前或之后。
優先級和關聯性僅影響運算符和操作數如何相互關聯——它們不影響表達式的計算順序。 優先規則規定
i = k++
被解析為
i = (k++)
而不是像
(i = k)++
后綴++
運算符有一個結果和一個副作用。 在表達式中
i = k++
k++
的結果是k
的當前值,它被分配給i
。 副作用是增加k
。
邏輯上等價於寫作
tmp = k
i = tmp
k = k + 1
需要注意的是,對i
的分配和對k
的更新可以以任何順序發生——這些操作甚至可以相互交錯。 重要的是i
在遞增之前獲得k
的值,並且k
遞增,不一定是這些操作發生的順序。
啊,這是一個很有趣的問題。 為了幫助您更好地理解,這就是實際發生的情況。
我將嘗試使用 C++ 中的一些運算符重載概念進行解釋,如果您不了解 C++,請耐心等待。
這就是重載postfix-increment
運算符的方式:
int operator++(int) // Note that the 'int' parameter is just a C++ way of saying that this is the postfix and not prefix operator
{
int copy = *this; // *this just means the current object which is calling the function
*this += 1;
return copy;
}
本質上, postfix-increment
運算符的作用是創建操作數的副本,增加原始變量,然后返回副本。
在您的i = k++
的情況下, k++
實際上確實首先發生,但返回的值實際上是k
(將其想象為 function 調用)。 然后將其分配給i
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.