C コードを考えてみましょうa = a = a
。代入のためのシーケンス ポイントがないため、このコードは での未定義の操作についてコンパイル時に警告を生成しa
ます。
a
ここで可能な値は何ですか? a
値を変更することはできなかったようです。ここに実際に未定義の動作がありますか、それともコンパイラが単に怠けているだけですか?
C コードを考えてみましょうa = a = a
。代入のためのシーケンス ポイントがないため、このコードは での未定義の操作についてコンパイル時に警告を生成しa
ます。
a
ここで可能な値は何ですか? a
値を変更することはできなかったようです。ここに実際に未定義の動作がありますか、それともコンパイラが単に怠けているだけですか?
シーケンス ポイント違反の未定義の動作の規則は、「値を変更できない」状況を例外としません。値が変化するかどうかは誰も気にしません。重要なのは、変数に何らかの書き込みアクセスを行うときは、その変数を変更していることです。変数に既に保持されている値を割り当てている場合でも、その変数の変更を実行しています。また、複数の変更がシーケンス ポイントで区切られていない場合、動作は未定義です。
おそらく、そのような「変更を伴わない変更」は問題を引き起こすべきではないと主張することができます。しかし、言語仕様自体はそのような詳細には関係しません。言語用語では、変数に何かを書き込むたびに、変数を変更しています。
さらに、質問で「あいまいな」という言葉を使用しているという事実は、動作が特定されていないと信じていることを意味しているようです。つまり、「変数の結果の値はあいまいです(またはあいまいではありません)」のように。ただし、シーケンス ポイント違反では、言語仕様は、結果がunspecifiedであると述べることに制限されていません。さらに進んで、動作をundefinedと宣言します。これは、これらのルールの背後にある理論的根拠が、変数の予測不可能な最終値以上のものを考慮に入れていることを意味します。たとえば、一部の架空のハードウェア プラットフォームでは、シーケンス化されていない変更により、コンパイラによって無効なコードが生成されるなどの結果が生じる可能性があります。
そのステートメントを実行した後に「未定義の動作」をするのは、実際にはプログラム全体です。の値だけではありませんa
- プログラムは、無限ループに入ったり、ガベージ出力を出力したり、クラッシュしたりするなど、何でもできます。
「未定義の動作」とは、C 標準がプログラムの動作に制限を設けていないことを意味します。そのコードを見たときに特定のコンパイラがどのように動作するかについて推論するのを止めるわけではありませんが、それでも有効な C プログラムではなく、それがコンパイラが警告していることです。
これは実際には未定義の動作です。a
任意の値を持つことができます。「壊れる可能性は考えられない」ということは、「動作することが保証されている」ということとは異なります。
int a = 42;
a = a = a;
未定義の動作です。
シーケンス ポイント規則は、コンパイラ メーカーの作業を容易にするために作成されました。
C 標準には、「動作があいまいになる場合、動作は未定義である」というルールはありません。問題となっている C 1999 の実際の規則は、「オブジェクトは、前のシーケンス ポイントと次のシーケンス ポイントの間で、式の評価によって最大 1 回変更された格納された値を持つ必要があります。さらに、以前の値は、保存する値を決定するためにのみ読み取られます。」</p>
あなたのコードはこの規則に違反しています: の値を変更しますa
。(3.1 3 の注記では、「変更」には、格納されている新しい値が以前の値と同じである場合が含まれます。)
それだけです。このコードの明確な解釈を理解できるかどうかは問題ではありません。規則に違反したことだけが重要です。ルールに違反しているため、動作は未定義です。
C 2011 では、ルールはより技術的な方法で記述されています。6.5 2 は次のように述べています。式の部分式に許容される順序が複数ある場合、順序付けのいずれかでそのような順序付けされていない副作用が発生した場合の動作は未定義です。」代入演算子がオブジェクトに値を格納するとき、それは実際には副作用です。(主な効果は、格納されている値に評価されることです。)したがって、C 2011 のこの規則は、C 1999 規則とほぼ同じことを述べています。同じオブジェクトに対して 2 つの副作用が発生することはありません。