23

数日前、ここで表現が

i = ++ i + 1

UB(未定義動作)を呼び出すかどうか。

最後に、「i」の値が2つのシーケンスポイント間で複数回変更されているため、UBを呼び出すという結論に達しました。

私は同じスレッドでJohannesSchaubとの話し合いに参加しました。彼によると

i =(i、i ++、i)+1 ------(1)/*UBも呼び出します*/

前の部分式の副作用は、iとi++の間およびi++とiの間のコンマ演算子'、'によってクリアされるため、(1)はUBを呼び出さないと言いました。

それから彼は次の説明をしました:

「はい、i ++の後のシーケンスポイントは、その前にすべての副作用を完了しますが、割り当ての副作用がi ++の副作用と重なるのを止めるものは何もありません。根本的な問題は、割り当ての副作用が、または割り当ての両方のオペランドの評価の前に、したがってシーケンスポイントはこれを保護することに関して何もできません:シーケンスポイントは半順序を誘発します:i ++の前後にシーケンスポイントがあるからといって、すべての副作用がシーケンスされるわけではありません私に関して

また、単にシーケンスポイントは意味がないことに注意してください。評価の順序は、コードの形式によって決定されません。これは、セマンティックルールによって決定されます。この場合、そのオペランドまたはそれらのオペランドの部分式の両方を評価することに関して、割り当ての副作用がいつ発生するかを示す意味規則はありません。

「太字」で書かれた声明は私を混乱させた。私の知る限りでは:

「シーケンスポイントと呼ばれる実行シーケンスの特定の指定されたポイントで、前の評価のすべての副作用が完了し、後続の評価の副作用が発生していないものとします。」

コンマ演算子も実行順序を指定するので、最後のiに到達したときにi ++の副作用はキャンセルされました。評価の順序が指定されていなければ、He(Johannes)は正しかったでしょう(ただし、コンマ演算子の場合は適切に指定されています) )。

だから私は(1)UBを呼び出すかどうかを知りたいだけですか?誰かが別の有効な説明をすることができますか?

ありがとう!

4

3 に答える 3

15

C標準では、代入演算子(C906.3.16またはC996.5.16代入演算子)について次のように述べています。

左オペランドの格納値を更新する副作用は、前のシーケンスポイントと次のシーケンスポイントの間で発生します。

私には、声明の中で次のように思われます。

i=(i,i++,i)+1;

代入演算子の「前」のシーケンスポイントは2番目のコンマ演算子になり、「次の」シーケンスポイントは式の終わりになります。したがって、式は未定義の動作を呼び出さないと思います。

ただし、この式:

*(some_ptr + i) = (i,i++,i)+1;

代入演算子の2つのオペランドの評価順序が未定義であるため、未定義の動作になります。この場合、代入演算子の副作用が発生したときに問題が発生するのではなく、左側のハンドルオペランドで使用されるiは、右側の前または後に評価されます。この評価の順序の問題は、最初の例では発生しません。これは、その式では、の値がi実際には左側で使用されていないためです。代入演算子が関心を持っているのは、の「左辺値」だけですi

しかし、私はまた、これはすべて大ざっぱであると思います(そして、関係するニュアンスの私の理解は十分に大ざっぱです)、誰かが他の方法で私を説得できても驚かないでしょう(どちらの点でも)。

于 2009-12-13T08:52:30.073 に答える
6

次の表現は間違いなく未定義の振る舞いをしていると思います。

i + ((i, i++, i) + 1)

その理由は、コンマ演算子は括弧内の部分式間のシーケンスポイントを指定しますが、そのシーケンスのどこで左側のオペランドの評価が+発生するかを指定しないためです。1つの可能性は、周囲のシーケンスポイント間でありi++、これは2つのシーケンスポイント間で書き込まれる5/4に違反しiますが、同じシーケンスポイント間で2回読み取られ、格納される値を決定するだけでなく、演算子の最初のオペランド+

これにも未定義の動作があります。

i += (i, i++, i) + 1;

さて、私はこの声明についてよくわかりません。

i = (i, i++, i) + 1;

同じ原則が適用されますがi、変更可能な左辺値として「評価」する必要があり、いつでも実行できますが、そのがこの一部として読み取られるとは確信していません。(または、式がUBを引き起こすために違反する別の制限がありますか?)

部分式(i, i++, i)は、格納される値を決定する一部として発生し、その部分式には、値をに格納した後のシーケンスポイントが含まれますii++保存する値を決定する前に、の副作用が完了する必要がないため、割り当ての副作用が発生する可能性のある最も早いポイントがないという方法はわかりません。

このシーケンスポイントiの値が最大で1回読み取られた後、に格納される値を決定するためだけに読み取られるiため、この最後の部分は問題ありません。

于 2009-12-13T10:30:26.897 に答える
5

i=(i,i++,i)+1 ------ (1) /* invokes UB as well */

未定義の動作は呼び出されません。の副作用i++は、次のシーケンスポイントの評価の前に発生します。これは、それに続くコンマで示され、割り当ての前にも発生します。

でも、いい言葉の数独。:-)

編集:ここにもっと詳しい説明があります。

于 2009-12-14T08:29:42.403 に答える