2014-01-09 36 views
3

下面是一個例子片斷:foo(i ++)+ foo(i ++)在ANSI C中未定義?

int i = 4,b;  
b = foo(i++) + foo(i++); 

我敢肯定它不是未定義,因爲那裏是foo調用之前的序列點。但是,如果我使用-Wall標誌編譯代碼,則會生成編譯器警告,其中說明warning: operation on 'i' may be undefined。我意識到它說may,但我只是想仔細檢查我是否正確。

+2

無論是否定義,您都不應該這樣編程。 –

+1

在調用'foo'之前有一個序列點,但是在'i ++'的兩次評估之間沒有序列點。生成的代碼可以評估第一個「i ++」,然後評估第二個「i ++」,然後執行兩個函數調用,然後添加結果。 C11(見[N1570草案](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf)改變了這種描述的方式並可能更清楚。) –

+1

所以它不是實際上沒有定義如果''foo''沒有副作用,並且只依賴於它的參數,如果我得到這個正確的。 –

回答

7

行爲未定義。

b = foo(i++) + foo(i++); 

正如你所說,有第一i++的評估和調用foo之間的序列點,同樣,第二i++的評價和呼叫foo之間。但是在兩個評估i++之間,或者更確切地說,在它們的副作用(修改i)之間沒有(必然)有序列點。

引述N1570草案的2011 ISO C標準,部分6.5.2.2p10的:

有功能 指定者的評價和實際參數之後,但在實際調用之前的順序點。在被調用函數主體的執行之前或之後,在調用函數(包括其他函數調用) 中的每個 評估不確定地關於被調用函數的執行而被排序爲 。

第二句話是顯著此處的i++兩個評價爲「不定 測序」相對於這兩個函數調用,這意味着它們可以之前或調用foo後發生。 (他們不是未測序,雖然,他們每個人發生之前或來電之後,但它是不明其中。)

而且6.5p2說:

如果一個標量的副作用對象相對於 相對於相同標量對象的不同副作用或使用相同標量對象的值進行計算的值不相同,行爲是 未定義。如果子表達式的子表達式有多個允許的排序順序,那麼如果在任何排序中出現這種不確定的副作用,則行爲是不確定的。

把這個在一起,一個符合標準的實現可以在這個順序計算表達式:

  1. 評估第一i++和保存在某個地方的價值。
  2. 評估第二個i++並將值保存在某處。
  3. 致電foo,將第一個保存的值作爲參數傳遞。
  4. 撥打foo,將第二個保存的值作爲參數傳遞。
  5. 添加兩個結果。
  6. 存儲總和b

步驟1和步驟2之間沒有順序點,它們都修改i,所以行爲是未定義的。

(這實際上是一個輕微的過於簡單;修改i的副作用,可從i++結果的確定中分離

底線:我們知道,

b = i++ + i++; 

是未定義行爲,原因是反覆解釋,函數調用中包含i++子表達式確實會添加一些序列點,但這些序列點不會分開i++的兩個評估,因此不會導致行爲成爲我們將被定義。

即使是最底層的行:請不要寫這樣的代碼。即使行爲被明確定義,但要證明它並確定行爲應該是什麼也是值得的。

+0

我讀過你複製和粘貼3-4次的標準部分,但仍然沒有真正理解它,儘管我覺得你的解釋是正確的。無論哪種方式,我不會這樣編程,我只是發佈這個問題,以更好地理解序列點。 – Justin

+1

如果您需要了解順序點,則編碼不好。 –

+0

@MartinJames這是一個純粹的學術問題。只是想更好地理解C。 – Justin

相關問題