2010-12-06 49 views
19

operator =不是一個序列點嗎?在C和C++中都是如此。爲什麼賦值運算符不是序列點的很好的理由?

我很難考慮一個反例。

+0

這需要編譯器在RHS之前計算LHS。你爲什麼要創造這種限制? – 2010-12-06 01:32:18

+2

`a = b = c = 0;` – pmg 2010-12-06 01:34:18

+7

一般而言,事物需要一個理由來** **一個序列點。他們不需要一個理由**而不是一個序列點;這是默認設置。 – 2010-12-06 01:59:22

回答

20

通過要求:

一般來說,東西都需要一個理由來序列點。他們不需要原因而不是是一個序列點;這是默認設置。

例如,由於短路行爲,&&必須是序列點:如果左側爲假,則不應評估右側。 (這不僅僅是優化,右邊可能有副作用,和/或取決於左邊是真實的,如ptr && ptr->data。)因此,左側必須首先評估,右側另一方面,爲了看看是否應該評估右側。

=這個理由不存在,因爲雖然對雙方都有「評估」(儘管雙方都有不同的限制:左邊必須是左值 - l不代表「左」,順便說一句;代表「位置」,如在內存中的位置 - 我們不能分配給臨時或文字),先評估哪一方並不重要 - 只要長因爲雙方在實際任務之前進行評估。

0

它是(有點)。運算符=(可以由工程師定義(也就是用戶定義的運算符=用於類類型))只是函數調用的語法糖。因此它具有與函數調用相同的「序列點」語義。

如果我們正在考慮內置類型,那麼我認爲這是一件好事。
您不想引入過多的順序點,因爲這會妨礙優化。

0

有很多理由不要求任何一方先評估另一方。一個更有趣的問題是,在賦值運算符本身做什麼之前,是否應該要求對雙方進行評估,並且有副作用。我會建議這樣的要求會緩解一些別名限制,但在某些情況下需要更多的編譯器工作。例如,假設「foo」和「bar」是指向地址重疊的大型結構的指針。聲明「* foo = * bar」將代表當前標準下未定義的行爲。如果在操作數評估和任務之間有一個序列點,那麼這樣的陳述將被保證「有效」。對於賦值運算符來說,這種保證要求更復雜,即使實際上指針永遠不會重疊,也需要更大更慢的代碼。

例子:

 
unsigned char foo[100]; 
typedef struct {int x, int y;} POINT; 
POINT *p1 = (POINT*)foo; 
POINT *p2 = (POINT*)(&(p1->y)); 

鑑於上述聲明,我認爲下面的語句具有嚴格定義的行爲表明不涉及任何未定義行爲。

 
    p1->y = somevalue; // Sets p2->x to somevalue 
    p2->x = somevalue; // Sets p1->y to somevalue 
    *p1 = mystruct;  // Sets p2->x to mystruct.y 
    *p2 = mystruct;  // Sets p1->x to mystruct.x 

下面的兩個語句,然而,會涉及未定義行爲:

 
    *p1 = *p2; 
    *p2 = *p1; 

如果有在平等的順序點標誌,編譯器將不得不採取比較p1和p2,要不然將源操作數複製到臨時位置,然後將其複製到目標位置。但是,該標準清楚地表明,上述兩個聲明都被認爲是未定義的行爲。該標準要求編譯器生成在將結構複製到非重疊結構時能夠正確工作的代碼,但對結構重疊時編譯器可能執行的操作沒有限制。一個編譯器,將處理器變成循環發送「Frink規則!」每個打開的TCP套接字都不會違反標準。

相關問題