2012-03-04 99 views
16

有關於C.不確定的行爲其中之一是(略有修改)'a [i] = i''總是會導致明確的行爲?

提出here幾個有趣的問題是否未定義的行爲下面這段代碼的結果?

int i = 0, *a = &i; // Line 1 
a[i] = i + 1;   // Line 2 

由於沒有具體答案的問題出現的這一部分,我想知道在C++中的行爲,我在這裏再次提高了。


規則#2從Undefined Behavior and Sequence Points

此外,該值是前一個值是唯一的訪問,以確定該值將被存儲

在上面的例子中

顯然被訪問兩次:a[i](lhs)和i(rhs),並且它們中只有一個(rhs)確定要存儲的值。

第2行是否違反上述規則並導致C++ 03中的未定義行爲?


有一些混亂至於i是否在第2行修改?

Yes it is modified!

回答

18

這將導致C++ 03中的未定義行爲以及C++ 11中明確定義的行爲。

C++ 03:未定義Behvaior

從C++ 03標準,第5第4段:

之前和下一序列點標量對象應具有其之間的存儲通過評估表達式最多修改一次值。此外,只有在確定要存儲的值時才能訪問先前值。

請注意第二句:以前的值i只能用於確定要存儲的值。但在這裏它也用於確定數組索引。因此,因爲此作業將修改爲i,因此a[0] = i+1已定義明確,而a[i] = i+1則不是。請注意,賦值不會生成一個序列點:只有完整表達式(分號)的結束。


C++ 11:明確界定行爲

C++ 11擺脫了序列點的概念,而是定義了評估是其前測序。

從標準,部分1.9第15段:

操作者的操作數的值的計算操作的結果的值計算之前進行測序。如果對標量對象的副作用相對於同一標量對象的另一副作用或使用同一標量對象的值進行值計算而言是不確定的,則行爲是未定義的。

賦值運算符的兩個操作數在實際賦值之前都被排序。因此a[i]i+1都會被評估,只有這樣纔會修改i。結果是明確的。

+4

+ 1對於C + +03和C++ 11的答案。 – 2012-03-04 17:05:58

3

int i = 0, *a = &i;

有聲明之間的序列點,此處因此沒有UB。但是請注意,以這種方式聲明/定義變量是一個壞主意。任何正常的編碼標準都會告訴你每行聲明一個變量。

a[i] = i;

i以任何方式不改變,因此在這裏沒有任何UB。

+1

那麼'a [i] = i + 1;'那麼? – Lazer 2012-03-04 15:43:04

+2

@Lazer:'i + 1'不會改變'i' – 2012-03-04 15:43:56

+0

@Lazer還是不行,因爲'i'還沒有被修改。 – 2012-03-04 15:44:06

0

在這種情況下未定義的行爲只會發生,如果你修改相同的內存地址沒有修改之間的序列點。具體而言,C99規格,第6.5節/ 2狀態,

之前和下一序列點之間的對象應具有由表達式的評估修飾的至多一次其 存儲的值。 此外,只有在訪問先前值時才能確定要存儲的值。

在你的情況下,序列點之間不會修改相同的存儲器地址,因此沒有未定義的行爲。

+2

這裏'i'正在被修改而沒有序列點(分號是我認爲第2行中唯一的序列點) – Lazer 2012-03-04 15:58:47

0

我想指出一件事情:a[i] = i不會總是會導致明確的行爲。行爲在指定的情況下被很好地定義的原因是因爲初始值ia

讓我解釋:

int i = 1, *a = &i; // Line 1, i initialized to anything other than 0 
a[i] = i + 1;   // Line 2, all of a sudden we are in buffer over/underflow 

對於i其他任何初始值,我們是從的i本身,它產生不確定的行爲訪問不同的內存位置。

+0

實際上當'a'指向單個整數'i'時,無論'如果'*(&i + i)= i'是UB,那麼根據interjay的回答,它是) – 2012-03-04 17:00:57

2

讓我們分解一下表達式a[i] = i + 1你會嗎?

= -- [] -- a 
    \  \_ i 
    \ 
    \_ + -- i 
     \_ 1 

有效,a[i]&i但是請注意,無論是a[i]也不i+1修改ii僅在執行=(分配本身)時才被修改。此功能生效之前

由於任何功能的操作數需要進行評估,這實際上等同於:

void assign(int& address, int value) { address = value; } 

assign(a[i], i + 1); 

這是事實,=之處在於它是內置有些特殊和不導致函數調用,仍然對兩個操作數的求值都是,實際賦值爲,因此它們在i被修改之前首先被計算,而a[i](它指向i位置)被賦值。

+0

interjay的答案是什麼,說只能訪問前一個值才能確定要存儲的值? – 2012-03-04 16:59:17

+0

這是在C++ 11中工作的方式,但不是C++ 03(儘管任何合理的C++ 03編譯器都可能以這種方式執行)。看到我更新的答案。 – interjay 2012-03-04 17:06:52

+0

@interjay:啊對,我對順序點從來沒有太精明,所以我只根據我的答案在最新的標準。很好的答案,謝謝你的徹底。 – 2012-03-04 18:22:43

0

不,它不。第一線具有序列點(逗號),所以它不是未定義的行爲:

int i = 0, *a = &i; 

第二行是完全正常的。

a[i] = i + 1; 

由於i + 1創建一個臨時值,i被修改一次,在分配。然而,這將是未定義的行爲:

a[i] = i++; 
相關問題