7

重複の可能性:
シーケンスされていない値の計算(別名シーケンスポイント)
未定義の動作とシーケンスポイント
演算子の優先順位と評価の順序

次の式が未定義の動作をどのようにもたらすかについて、私はまだ頭を悩ませようとしています。

a = a++;

これについてSOを検索したところ、次の質問が見つかりました。

シーケンスポイントと演算子の優先順位の違いは?0_o

私はすべての答えを読み通しましたが、それでも詳細に問題があります。回答の1つは、上記のコード例の動作を、a変更方法の観点からあいまいであると説明しています。たとえば、次のいずれかになります。

a=(a+1);a++;
a++;a=a;

aの変更があいまいになるのは何ですか?これは、さまざまなプラットフォームのCPU命令と関係があり、オプティマイザーが未定義の動作をどのように利用できるかを示していますか?言い換えれば、生成されたアセンブラのために未定義のように見えますか?

コンパイラが使用する理由はわかりa=(a+1);a++;ません。奇妙に見え、あまり意味がありません。このように動作させるためのコンパイラは何を持っているでしょうか?

編集:

明確にするために、私は何が起こっているのかを理解しています。演算子の優先順位(基本的に式の評価の順序を定義する)にルールがある場合に、それがどのように未定義になるかを理解していません。この場合、割り当ては最後にa++行われるため、割り当てる値を決定するために、最初に評価する必要がありますa。したがって、私が期待しているaのは、修正後の増分中に最初に変更されますが、次に割り当てる値が生成されることですa(2番目の変更)。しかし、演算子の優先順位の規則は、動作を非常に明確にしているように見えます。未定義の動作をするための「小刻みに動く余地」がある場所を見つけることができません。

4

6 に答える 6

8

リンクした質問の最初の回答は、何が起こっているのかを正確に説明しています。より明確にするために言い換えてみます。

演算子の優先順位は、式を介した値の計算の順序を定義します。式の結果(a++)はよく理解されています。

ただし、変数の変更はa 式の一部ではありません。はい、そうです。これはあなたが理解するのに苦労している部分ですが、それは単にCとC++がそれを定義する方法です。

式は値になりますが、一部の式には副作用があります。式a = 1の値は。ですが、変数をに設定するという副作用1もあります。CとC++が物事をどのように定義するかに関しては、これらは2つの異なるステップです。同様に、値と副作用があります。a1a++

シーケンスポイントは、それらのシーケンスポイントの後に評価される式に副作用がいつ表示されるかを定義します。演算子の優先順位は、シーケンスポイントとは関係ありません。それがC/C++が物事を定義する方法です。

于 2012-09-07T15:47:26.393 に答える
1

これは単純すぎる説明かもしれませんが、コードが「a」で「完了」したときに解決する方法がないためだと思います。それは増分または割り当ての後に行われますか?解像度は円形になります。増分後の割り当てにより、増分値が適用されるときのセマンティクスが変更されます。つまり、コードは「a」がインクリメントされるまで「a」で実行されませんが、割り当てが行われるまでaはインクリメントされません。これは、ほとんど言語バージョンのデッドロックです。

私が言ったように、それは素晴らしい「学術的」説明ではないと確信していますが、それは私が自分の耳の間にそれを詰め込む方法です。それがなんとか役立つことを願っています。

于 2012-09-07T15:43:24.107 に答える
1

優先順位ルールは、式が評価される順序を指定しますが、評価中に副作用が発生する必要はありません。これらは、次のシーケンスポイントの前であればいつでも発生する可能性があります。

この場合、増分の副作用は割り当ての前でも後でもシーケンスされないため、式の動作は未定義です。

于 2012-09-07T15:47:08.193 に答える
0

ステートメントの基本的な問題を実行してみましょうa = a++。次のすべてを達成したいと考えています。

  • 値を決定します( 、#1aの戻り値)a++

  • 増分( 、#2aの副作用)a++

  • 古い値をに割り当てますa(割り当ての効果、#3)

これをシーケンスする方法は2つあります。

  1. オリジナルaa(no-op)に保存します。次にインクリメントしますa。と同じa = a; ++a;。これはシーケンス#1-#3-#2です。

  2. 評価a、増分a、元の値をに戻しaます。と同じb = a; ++a; a = b;。これはシーケンス#1-#2-#3です。

所定の順序がないため、どちらの操作も可能です。しかし、最終的な結果は異なります。どちらのシーケンスも、他のシーケンスより自然ではありません。

于 2012-09-07T16:17:02.103 に答える
0

ここでのポイントは、Intel Itaniumなどの一部のCPUアーキテクチャでは、これら2つの操作をコンパイラによって命令レベルで並列化できるということですが、構造を明確に定義すると、それが禁止されます。シーケンスポイントの仕様の時点では、これらのアーキテクチャはほとんど架空のものであり、Itaniumはフロップであったため、2012年の時点で、これらの多くは言語の不必要な複雑さであると考えられます。まだ一般的に使用されているアーキテクチャには基本的に不利な点はありません。Itaniumの場合でも、パフォーマンスの利点は最小限であり、それを利用できるコンパイラを作成することの頭痛の種は非常に大きかったです。

また、C ++ 11では、シーケンスポイントが前にシーケンスされ、後にシーケンスされたものに置き換えられたため、このような状況がより明確になりました。

于 2012-09-07T15:50:46.300 に答える
0

そのステートメントa=a++には、2つの結果と、2つの割り当てがあります。

a=a

(ポストインクリメントのため)そして

a=a+1

これらの割り当てにより、は明らかに異なる最終値になりaます。

c標準のドラフト作成者は、2つの割り当てのどちらをa最初に、どちらを2番目に書き込むかを指定していなかったため、コンパイラーの作成者は、どのような状況でも自由に選択できます。

結果として、それ(この特定のステートメント)はクラッシュしませんが、プログラムは特定の値を持つことに依存できなくなります。

于 2012-09-07T15:58:32.860 に答える