名前空間
変種
操作

評価順序

提供: cppreference.com
< c‎ | language

あらゆる C の演算子の被演算子の評価順序 (関数呼び出し式の関数引数の評価順序を含む) およびあらゆる式の部分式の評価順序は (下で述べられている点を除いて) 未規定です。 コンパイラはそれらを任意の順序で評価し、同じ式がもう一度評価されるときに別の順序が選ばれることもあります。

C には左から右に評価するとか右から左に評価するといった概念はありません。 これを演算子の左結合や右結合と混同しないでください。 式 f1() + f2() + f3() は、 + 演算子が左結合であるため、 (f1() + f2()) + f3() と解析されますが、実行時には f3 の関数呼び出しは最初に評価されるかもしれませんし、最後かもしれませんし、 f1()f2() の間に評価されるかもしれません。

目次

[編集] 定義

[編集] 評価

それぞれの式または部分式についてコンパイラが行う評価には2種類あります (どちらもオプショナルです)。

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

式が副作用を生成せず、値も使用されないとコンパイラが断定できる場合、その式は評価されないかもしれません

[編集] 順序付け

「先行配列」は、非対称で推移的な、同じスレッド内の評価間の二項関係です (アトミック型およびメモリバリアが影響する場合はスレッドを超えることがあります)。

  • 部分式 E1 と E2 の間に副作用完了点が存在する場合、 E1 の値計算と副作用の両方が E2 のすべての値計算および副作用に対して先行配列されます。
  • 評価 A が評価 B に対して先行配列される場合、 A の評価は B の評価が始まる前に完了します。
  • A が B に対して先行配列されず、 B が A に対して先行配列される場合、 B の評価は A の評価が始まる前に完了します。
  • A が B に対して先行配列されず、 B が A に対して先行配列されない場合は、2つの可能性があります。
    • A の評価と B の評価が配列されない。 それらは任意の順序で行われる可能性があり、オーバーラップする可能性もあります (単一の実行のスレッド内で、コンパイラは A を構成する CPU 命令と B を構成する CPU 命令をインターリーブできます)。
    • A の評価と B の評価が不定に配列される。 それらは任意の順序で行われる可能性がありますが、オーバーラップすることはありません。 A が B より前に完了するか、 B が A より前に完了するかの、いずれかです。 次回、同じ式が評価されるときに、順序が逆になる可能性はあります。
(C11およびそれ以降)

[編集] ルール

1) すべての関数引数および関数指示子の評価の後、実際の関数呼び出しの前に、副作用完了点があります。
2) 二項演算子 && (論理積)、 || (論理和)、 , (コンマ) の第1被演算子 (左の引数) の評価の後、第2被演算子 (右の引数) の評価の前に、副作用完了点があります。
3) 条件演算子 ?: の第1被演算子 (左の引数) の評価の後、第2または第3被演算子 (どちらか評価される方) の評価の前に、副作用完了点があります。
4) 完全式 (部分式でない式、一般的にはセミコロンで終わるものや if/switch/while/do の制御文) の評価の後、次の完全式の評価の前に、副作用完了点があります。
5) 完全な宣言子の終わりに副作用完了点があります。
6) ライブラリ関数の return の直前に副作用完了点があります。
7) 書式化入出力の各変換指定子に紐付けられたアクションの後に副作用完了点があります (特に、 scanf が同じ変数に異なるフィールドを書き込む場合と、 printf%n を用いて同じ変数を読み書きまたは複数回変更する場合)。
8) ライブラリ関数 qsort および bsearch によって行われる比較関数の毎回の呼び出しの前および直後、および比較関数のあらゆる呼び出しと qsort によって行われる紐付けられたオブジェクトの移動の間に、副作用完了点があります。
(C99およびそれ以降)
9) 任意の演算子の被演算子の値計算 (副作用ではない) は、その演算子の結果の値計算 (副作用ではない) に対して、先行配列されます。
10) 直接代入演算子およびすべての複合代入演算子の副作用 (左の引数の変更) は、左右両方の引数の値計算 (副作用ではない) に対して、先行配列されます。
11) 後置インクリメントおよび後置デクリメント演算子の値計算は、その副作用に対して、先行配列されます。
12) 別の関数呼び出しに対して先行配列も後続配列もされない関数呼び出しは、不定に配列されます (たとえ関数がインラインでも、異なる関数呼び出しを構成する CPU 命令はインターリーブできません)。
13) 初期化リスト式において、すべての評価は不定に配列されます。
14) 不定に配列された関数呼び出しについて、複合代入演算子の演算、前置と後置両方のインクリメントおよびデクリメント演算子は、単一評価です。
(C11およびそれ以降)

[編集] 未定義動作

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

[編集] 関連項目

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

評価順序C++リファレンス