名前空間
変種
操作

評価順序

From cppreference.com
< c‎ | language

C言語のあらゆる演算子のオペランドの評価順序は、関数呼び出し式の関数引数の評価順序、およびあらゆる式内の部分式の評価順序を含め、未規定です(下記に記載されている場合を除く)。コンパイラは任意の順序でそれらを評価し、同じ式が再度評価される際に別の順序を選択する可能性があります。

C言語には、左から右または右から左への評価という概念はありません。これは、演算子の左から右および右から左への結合性と混同してはなりません。式 f1() + f2() + f3() は、演算子+の左から右への結合性のため、(f1() + f2()) + f3() と解析されますが、f3 への関数呼び出しは、実行時に最初、最後、または f1()f2() の間に評価される可能性があります。

目次

[編集] 定義

[編集] 評価

コンパイラが各式または部分式に対して実行する評価には、2つの種類があります(どちらも任意です)

  • 値の計算: 式が返す値の計算。これには、オブジェクトの識別(左辺値評価)の決定、またはオブジェクトに以前に割り当てられた値の読み取り(右辺値評価)が含まれる場合があります。
  • 副作用: volatile 左辺値によって指定されたオブジェクトへのアクセス(読み取りまたは書き込み)、オブジェクトへの変更(書き込み)、アトミックな同期(C11以降)、ファイルの変更、浮動小数点環境の変更(サポートされている場合)、またはそれらの操作のいずれかを実行する関数の呼び出し。

式によって副作用が生成されず、コンパイラがその値が使用されないと判断できる場合、その式は評価されないことがあります。

[編集] 順序付け

"sequenced-before" は、同じスレッド内の評価間の非対称で推移的な対の関係です(アトミック型とメモリバリアが関与する場合、スレッドをまたぐことがあります)。

  • 部分式E1とE2の間にシーケンスポイントが存在する場合、E1の値計算と副作用の両方は、E2のすべての値計算と副作用の前にsequenced-beforeされます。
  • 評価Aが評価Bの前にシーケンスされている場合、Bの評価が始まる前にAの評価が完了します。
  • AがBの前にシーケンスされておらず、BがAの前にシーケンスされている場合、Aの評価が始まる前にBの評価が完了します。
  • AがBの前にシーケンスされておらず、BがAの前にシーケンスされていない場合、2つの可能性が存在します。
    • AとBの評価は順序付けられていない(unsequenced):これらは任意の順序で実行され、重複する可能性があります(単一の実行スレッド内では、コンパイラはAとBを構成するCPU命令をインターリーブする可能性があります)。
    • AとBの評価は不確定に順序付けられている(indeterminably-sequenced):これらは任意の順序で実行される可能性がありますが、重複することはありません。AがBの前に完了するか、BがAの前に完了します。同じ式が次に評価されるときには、順序が逆になる可能性があります。
(C11 以降)

[編集] 規則

1) すべての関数引数と関数指定子の評価の後、実際の関数呼び出しの前にシーケンスポイントがあります。
2) 以下の二項演算子 && (論理AND)、 || (論理OR)、および , (カンマ) の最初の(左)オペランドの評価の後、2番目の(右)オペランドの評価の前にシーケンスポイントがあります。
3) 条件演算子 ?: の最初の(左)オペランドの評価の後、2番目または3番目のオペランド(どちらが評価されるか)の評価の前にシーケンスポイントがあります。
4) 完全な式(部分式ではない式:通常はセミコロンまたは if/switch/while/do制御ステートメントで終わるもの)の評価の後、次の完全な式の前にシーケンスポイントがあります。
5) 完全な宣言子の最後にシーケンスポイントがあります。
6) ライブラリ関数の戻りの直前にシーケンスポイントがあります。
7) 書式付きI/Oにおける各変換指定子に関連付けられたアクションの後(特に、scanfが異なるフィールドを同じ変数に書き込み、printf%nを使用して同じ変数を複数回読み書きまたは変更することが適切に形成されている)シーケンスポイントがあります。
8) ライブラリ関数qsortおよびbsearchによって行われる比較関数の各呼び出しの前と直後、および比較関数への呼び出しとqsortによって行われる関連オブジェクトの移動の間にシーケンスポイントがあります。
(C99以降)
9) 任意の演算子のオペランドの値計算(副作用は除く)は、演算子の結果の値計算(副作用は除く)の前にシーケンスされます。
10) 直接代入演算子とすべての複合代入演算子の副作用(左引数の変更)は、左右両引数の値計算(副作用は除く)の後にシーケンスされます。
11) 後置インクリメントおよび後置デクリメント演算子の値計算は、その副作用の前にシーケンスされます。
12) 他の関数呼び出しの前にシーケンスされておらず、後にシーケンスされていない関数呼び出しは不確定にシーケンスされます(異なる関数呼び出しを構成するCPU命令は、関数がインライン化されている場合でもインターリーブできません)。
13) 初期化リスト式では、すべての評価は不確定にシーケンスされます。
14) 不確定にシーケンスされた関数呼び出しに関して、複合代入演算子の操作、およびインクリメントおよびデクリメント演算子の前置形と後置形の両方は、単一の評価です。
(C11 以降)

[編集] 未定義の動作

1) スカラーオブジェクトに対する副作用が、同じスカラーオブジェクトに対する別の副作用に対して順序付けられていない場合、動作は未定義です。
i = ++i + i++; // undefined behavior
i = i++ + 1; // undefined behavior
f(++i, ++i); // undefined behavior
f(i = -1, i = -1); // undefined behavior
2) スカラーオブジェクトに対する副作用が、同じスカラーオブジェクトの値を使用する値計算に対して順序付けられていない場合、動作は未定義です。
f(i, i++); // undefined behavior
a[i] = i++; // undefined bevahior
3) 上記の規則は、部分式の少なくとも1つの許容される順序付けが、そのような順序付けられていない副作用を許容する限り適用されます。

[編集] 関連項目

演算子の優先順位:ソースコード表現から式がどのように構築されるかを定義します。

C++ ドキュメント評価の順序について)
English 日本語 中文(简体) 中文(繁體)