CおよびC ++での+ =の結果は何ですか?


93

私は次のコードを持っています:

#include <stdio.h>
int main(int argc, char **argv) {
    int i = 0;
    (i+=10)+=10;
    printf("i = %d\n", i);
    return 0;
}

gccを使用してCソースとしてコンパイルしようとすると、エラーが発生します。

error: lvalue required as left operand of assignment

しかし、g ++を使用してC ++ソースとしてコンパイルした場合、エラーは発生せず、実行可能ファイルを実行すると次のようになります。

i = 20

なぜ異なる動作ですか?


85
異なる言語、異なる構文規則?個人的には、コードレビューでそのコードを拒否します。
最大

7
このimoのようなコードは避けてください。
allaire、

1
間違いなく、コードはクリーンではなく、「実際の」開発では避ける必要があります。それにもかかわらず、私は同じ行動を観察し、その理由を知りたいのです。
ulidtko

9
これは実際のソフトウェアから抜粋したコードではありません。これは私が誤って偶然見つけたねじれです。
スベトリンムラデノフ

3
@JohnDibling賛成投票は具体的には(i + = 10)+ = 10だと思います。正当なコードがどの言語なのかわかりません。また、C ++が実際にコンパイルしていると彼が言っているという事実には興味をそそられます。
Tony318

回答:


133

複合代入演算子のセマンティクスは、CとC ++では異なります。

C99標準、6.5.16、パート3:

代入演算子は、左のオペランドで指定されたオブジェクトに値を格納します。割り当て式は、割り当て後に左のオペランドの値を持っていますが、左辺値ではありません。

C ++ 5.17.1の場合:

代入演算子(=)と複合代入演算子はすべて、右から左にグループ化されます。すべての左オペランドとして変更可能な左辺値が必要であり、割り当てが行われた後、左オペランドの型と値を含む左辺値を返します。

編集:(i+=10)+=10 C ++での動作はC ++ 98では未定義ですが、C ++ 11では適切に定義されています。標準の関連部分については、NPEの質問に対するこの回答を参照してください。


正しい。1つは結果の値を返し、もう1つは変数(アドレス)を返します
texasbruce 2012年

7
重要:これ(i+=10)+=10はC ++では未定義の動作です。@ aixの回答を参照してください。
DavidRodríguez-

@DavidRodríguez-dribeas 未定義ではなく、特定されていないという意味ですか?
dasblinkenlight

4
@dasblinkenlight:いいえ、彼は未定義を意味しました。C ++ 03以前のバージョンでは、式の左辺値の結果を変更すると、シーケンスポイントが介在しないため、すべてのコンパイラで予期しない動作をします。指定されていない場合、予測可能な動作になりますが、コンパイラによって動作は異なります。
ジャスティン

2
これはint f(int &y); f(x += 10);、変更された変数への参照を関数に渡すなどの設定で役立ちます。
Phil Miller

51

無効なCコードであることに加えて、行

(i+=10)+=10;

iシーケンスポイント間で2回変更されるため、CとC ++ 03の両方で未定義の動作が発生します。

C ++でのコンパイルが許可されている理由について:

[C ++ N3242 5.17.1]代入演算子(=)および複合代入演算子はすべて右から左にグループ化されます。すべての左オペランドとして変更可能な左辺値が必要であり、左オペランドを参照する左辺値を返します。

同じ段落は続けてそれを言う

すべての場合において、割り当ては、右と左のオペランドの値計算の後、割り当て式の値計算の前にシーケンスされます。

これは、C ++ 11では、式の動作が未定義ではなくなったことを示しています。


3
まあ、それはシーケンスポイントのため、確かにUBです。これはC でも無効なコードですが(C ++ではありません)、シーケンスポイントとは無関係であり、コンパイラーがキャッチする必要があります。
コンラートルドルフ

2
@KonradRudolph:いいえ、不正なコードとは対照的に、コンパイラは未定義の動作をキャッチする義務はありません。「コンパイラーに捕らえられるべき」部分は私たちが反対するところです。

2
シーケンスポイントはC ++ 11には存在しないため、UBの実際の理由は、シーケンスされていない変更が2つあるためですi
Mankarse、

4
これが未定義の動作であることは誤りです。割り当て式の値計算の前に割り当てがシーケンスでなかった場合、i = j+=1結果は不確定な値になります。同じ段落から、「すべての場合において、割り当ては、右と左のオペランドの値計算の後、割り当て式の値計算の前にシーケンスされます。」したがって、(i+=10)+=10明確に定義されていますi += 10; i += 10;。一方、(i+=10)+=(i+=10)UBです。
bames53

2
:コメンターの間にこの上でいくつかの意見の相違があったことを私が見たので、私は別の質問としてこれを投稿したstackoverflow.com/questions/10655290/...
NPE
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.