web-dev-qa-db-ja.com

CおよびC ++では、「a = b、++ a;」のようなコンマ演算子を使用した式です。未定義?

次の3つのCコードのスニペットをご覧ください。

_1) a = b + a++
2) a = b + a; a++
3) a = b + a, a++
_

例1が非常に悪いことであることは誰もが知っており、未定義の動作を明確に呼び出しています。例2には問題はありません。例3に関する質問です。この種の式では、コンマ演算子はセミコロンのように機能しますか? 2と3は同等ですか、3は1と同じように未定義ですか?

具体的には、free(foo), foo = barのようなものに関してこれを検討していました。これは基本的に上記と同じ問題です。 fooが再割り当てされる前に解放されていることを確認できますか、またはこれは明確なシーケンスポイントの問題ですか?

どちらの例もほとんど無意味であり、セミコロンを使用してそれを実行する方がはるかに理にかなっています。私はただ好奇心を求めています。

22
Roflcopter4

ケース3は明確に定義されています。

コンマ演算子_,_に関する C標準 のセクション6.5.17には、次のことが記載されています。

2コンマ演算子の左オペランドは、void式として評価されます。 評価と右のオペランドの評価の間にシーケンスポイントがあります。次に、右のオペランドが評価されます。結果にはそのタイプと値があります

C++ 11標準 のセクション5.14 p1には同様の言語があります。

コンマで区切られた式のペアは、左から右に評価されます。左の式は破棄された値の式です。 左式に関連付けられたすべての値の計算と副作用は、右式に関連付けられたすべての値の計算と副作用の前にシーケンスされます。結果は、右側のオペランドのタイプと値です。結果は、右側のオペランドと同じ値カテゴリになり、右側のオペランドがglvalueとビットフィールドの場合、ビットフィールドになります。

シーケンスポイントのため、式_a = b + a_の_a++_は、_a = b + a, a++_の前に完全に評価されることが保証されています。

free(foo), foo = barに関しては、これにより、新しい値が割り当てられる前にfooが解放されることも保証されます。

38
dbush

_a = b + a, a++;_は明確に定義されていますが、a = (b + a, a++);は未定義にすることができます。

まず、演算子の優先順位により、式は_(a = (b+a)), a++;_と同等になります。ここで、_+_が最も優先順位が高く、次に_=_、_,_が続きます。コンマ演算子には、左オペランドと右オペランドの評価の間にシーケンスポイントが含まれます。したがって、コードは、面白くはありませんが、次のものと完全に同等です。

_a = b + a;
a++;
_

これはもちろん明確に定義されています。


代わりにa = (b + a, a++);と書いた場合、コンマ演算子のシーケンスポイントは日を保存しません。そのため、式は

_(void)(b + a);
a = a++;
_
  • CおよびC++ 14以前では、_a = a++_はシーケンス化されていません(C11 6.5.16/3を参照)。つまり、これは未定義の動作です(C11 6.5/2あたり)。 C++ 11とC++ 14の形式が不適切であいまいであることに注意してください。
  • C++ 17以降では、_=_演算子のオペランドは右から左に順序付けられますが、これはまだ明確に定義されています。

これはすべて、C++演算子のオーバーロードが発生しないことを前提としています。その場合、オーバーロードされた演算子関数のパラメーターが評価され、関数が呼び出される前にシーケンスポイントが発生し、そこから何が起こるかはその関数の内部に依存します。

11
Lundin