2010-07-24 75 views
9

我有以下代碼表達式的行爲:已定義還是未定義?

int m[4]={1,2,3,4}, *y; 
y=m; 
*y = f(y++); // Expression A 

我的朋友告訴我,Expression A有一個明確的行爲,但我不知道他是否是正確的。

根據他的功能f()介紹了一個sequence point之間,因此行爲是明確的。

有人請澄清。

P.S:我知道我們不應該爲實際目的編寫這樣的代碼。這只是爲了學習的目的。 :)

+3

我說不。這就是'gcc'告訴我的。 – kennytm 2010-07-24 16:03:00

回答

15

充其量,有問題的代碼有未指定的行爲。對於賦值操作符,「操作數的求值順序未指定」(C99§6.5.16/ 4)。

如果首先評估左操作數,則f(y++)的結果將存儲在m[0]中。如果首先評估右操作數,則結果將存儲在m[1]中。

至於該行爲是否是不確定的,有關段落是:

的前面和後面的序列點的對象應通過表達式的評估有其儲值作案網絡版最多一次之間。此外,先前值只能讀取以確定要存儲的值(C99§6.5/ 2)。

如果左側先進行計算,然後我們觸犯第二句的運行,因爲排序是:

  1. y值讀取左側取消對它的引用
  2. 的在右側讀取y以增加它的值
  3. 在函數的參數評估之後有一個序列點(所以,y++的副作用已完成,並且y被寫入)

在步驟1 y「先有值」被讀出,但比其它目的,「確定的值被存儲。」因此,行爲確實是未定義的,因爲一個有效的評估訂單會產生未定義的行爲。

+2

+1。我相信它甚至是未定義的,因爲'* y'和'y ++'的評估是不確定的,後者對'y'產生副作用。 – avakar 2010-07-24 16:41:41

+0

謝謝詹姆斯,你的回答非常好。 AC :-) – 2010-07-25 03:07:34

0

編輯:這是不正確的,但我留在這裏,因爲評論中的下面的討論有點照亮,我希望有價值。

它基於C(或C++)中運算符的評估順序定義良好。

賦值力首先評估表達式的右邊。函數應用程序首先強制評估它的參數,所以效果似乎很合理清晰(儘管我沒有嘗試過運行它,所以請隨時糾正我!)。我們可以重寫這個使用臨時變量(我會打電話給他們T0和T1),我相信這可能是一個更清晰一點:

t0 = y++; 
t1 = f(t0); 
*y = t1; 

術語「序列點」是有點紅鯡魚。序列點並不是真正創建的,而只是爲語言定義了嚴格的評估順序的結果。

編輯:雖然這個答案似乎智力滿意,詹姆斯McNellis的回答引用了相關片C99規範的這些規定,分配的計算順序是明確。充分信任他實際檢查他的事實。我將把我的答案從「它是明確定義的」修改爲「它可能對某個特定的編譯器有很好的定義」,因爲我認爲大多數編譯器不可能經常更改它們發出這樣的代碼的順序(我說「可能」來解釋任何非常激進的優化)。

+2

_Assignment首先評估表達式的右側。這不是事實:評估順序未指定。 – 2010-07-24 16:27:45

+1

它在哪裏聲明必須首先評估作業的右側? – jalf 2010-07-24 16:28:20

+0

謝謝,我剛剛編輯了我的答案,以解釋這一點,並感謝教我一些關於C的新東西:) – Gian 2010-07-24 16:28:45

1

的表達沒有很好地定義:

中表達的有效解釋是:

(1) int* t0 = y++; 
(2) int t1 = f(t0); 
(3) int& t2 = *y; 
----------------- 
t2 = t1; 

一個同樣有效表達的解釋是:

(1) int& t2 = *y; 
(2) int* t0 = y++; 
(3) int t1 = f(t0); 
----------------- 
t2 = t1; 

這兩者都是vaalid並生成不同的結果。所以表達式有未定義的結果。

+1

一般而言,表達式可能有兩個不同結果的事實僅僅意味着結果是*未指定*。然而,在這種特殊情況下,結果確實是不確定的,因爲't0'和't2'的計算可以以任何順序發生,'t0'對'y'有副作用,而't2'使用'y'的值。 – avakar 2010-07-24 16:51:38

13

對於引入序列點的函數調用你是絕對正確的。但是,這個順序點並不能保存你的情況。

考慮一個簡單的例子第一

i = some_function(i++); 

是否有效?是的。爲什麼?這是有效的,因爲函數引入的序列點(您正在討論的那個)將兩個i之間的修改相互分開,從而使代碼有效。沒有評估此表達式的順序,這會導致i被修改兩次而沒有中間順序點。

但是,讓我們回到你的變種

*y = f(y++); 

在該序列點存在,以及這種情況。但是,該語言不保證操作符=的評估順序(意思是:該語言不能保證賦值運算符的哪個操作數首先被評估:左或右)。編譯器可以先評估左側(*y),然後再函數參數(y++),然後調用該函數,然後執行實際分配。在這種潛在情況下,前兩個步驟 - 讀取y和修改y - 不會被序列點分開。因此,行爲是不確定的。

+1

@本Voigt:我在談論'='操作符的操作數評估的相對順序,即LHS還是RHS是首先評估的。 – AnT 2010-07-25 05:36:25