2011-02-07 9 views
3

誰もが知っているように、これはゼロをループ:なぜ `x--> 0`は未定義の振る舞いではなく、` x = x - `は?

while (x-- > 0) { /* also known as x --> 0 */ 
    printf("x = %d\n", x); 
} 

しかしx = x--undefined behaviourが得られます。


両方の例は、私が推測するがないx--のいくつかの「リターン」の値を、必要とします。 x-- > 0が定義されていますが、x = x--はどのように定義できますか?

+4

正式な理由の他に、 'x = x - ; 'と書くことは意味をなさないことをご理解ください。 'x - ;'を単独で書くか、 'x = x - ;'の2番目の '-'を' 1' ... –

+0

@Rに置き換えてください。私はそれを別の言い方で尋ねたほうがよいかもしれません: 'x = 0 - 'x = x - 'のように定義されていないのはなぜですか?' –

+2

形式的な観点から、 'x'は一度だけ変更されるためです。常識的な見地からは、これが '--'演算子の全体点であるため、それは変更されて左辺値になり、古い値が得られます。何も怪しげなことはない。 –

答えて

19

x = x--では、介入するシーケンスポイントなしでxの値を2回修正しているためです。したがって、操作の順序は定義されていません。 x-- > 0では、xの値が一度変更され、x--を評価した結果が、デクリメント前にxの値になることが明確に定義されています。

+0

ああ、 'x - 'は 'x - 1'と評価されますが、' x = x - 'の順序は定義されていませんか? –

+0

@Radek S:ハァッ? 'x - 'は 'x'をまったく返しません。 'x - 'は 'x'の元の値*を返します。最初に、元の値は*減少前の値*です(あなたはどこから来たのですか?)。第二に、それは 'x'ではありません。 'x'の元の*値*です。変数の*変数*と*値*の違いを理解していただければと思います。 – AnT

+2

@Radek S: 'x = x - 'には何も定義されていません。言語は 'x = x - 'は定義されていないと言います。つまり、何かの「秩序」だけでなく、すべてが未定義であることを意味します。 – AnT

7

x-の「戻り値」が必要であるという考えがどこにあるのか分かりません。まず、あなたが何を意味するのかはっきりとは分かりません。第二に、あなたが何を意味するかにかかわらず、これはx = x--の未定義の動作の原因とは関係がないように思われます。

x = x--は、介入するシーケンスポイントなしでxを2回修正しようとするため、未定義の動作が発生します。ここでは「戻り値」の「必要性」はありません。

x = x--の根底にある問題は、未定義の順序で未定義の瞬間に発生する2つの副作用があることです。 1つの副作用が代入演算子によって導入されます。 postfix --演算子によって別の副作用が導入されました。両方の副作用は同じ変数xを変更しようとし、お互いに一般的に矛盾します。そのため、このような場合の動作は定義されていないと宣言されています。例えば

xの元の値が5れた場合、あなたの発現が同時に4(デクリメントの副作用)と5(割り当ての副作用)の両方になることxを必要とします。言うまでもなく、x45に同時になることは不可能です。

UBには、このような直接的な矛盾(45など)は必要ありませんが。介入するシーケンスポイントなしで同じ変数に当たる2つの副作用があるたびに、これらの副作用が変数マッチに入れようとしている値であっても、その動作は未定義です。

+1

例として、 '(x = 1)+(x = 1)'は未定義の振る舞いを呼び出します。 –

2

他の回答に何かを追加するだけで、this wikipedia pageのシーケンスポイントについて読んでみてください。

3

これを理解するには、シーケンスポイントの基本的な理解が必要です。何のシーケンスポイントがないので、それが再びxに割り当てられる前にxの値が変更されるという保証はありません=オペレータについてhttp://en.wikipedia.org/wiki/Sequence_point

:このリンクを参照してください。

x-- > 0の条件をチェックすると、が一度しか変更されないため、x--が評価され、その値が関係演算子評価で使用されるため、未定義の動作が発生する可能性はありません。

1

私は読むことをお勧めしますhttps://stackoverflow.com/a/21671069/258418。もし=sequence pointではなく、あなたがリンクしている回答からシーケンスポイントで区切られていない限り、コンパイラは操作をインターリーブすることができます。つまり、次の2つのシーケンスが有効です。

一般に
load i to reg 
increment i 
assign reg to i 
=> i has previous value of i 

load i to reg 
assign reg to i 
increment i 
=> i has value of previous value of i + 1 

:(これは、プリ/ポスト++ /によってmodiying含む - )を割り当てる避ける一つの式で二回同じ変数です。

関連する問題