Namespaces
Variants

Order of evaluation

From cppreference.net

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

C言語には左から右、あるいは右から左への評価順序という概念は存在しません。これは演算子の左結合性や右結合性とは混同しないでください:式 f1 ( ) + f2 ( ) + f3 ( ) ( f1 ( ) + f2 ( ) ) + f3 ( ) と解析されます。これは operator + の左結合性によるものですが、実行時には関数呼び出し f3 ( ) が最初に評価されることも、最後に評価されることも、 f1 ( ) f2 ( ) の間に評価されることもあります。

目次

定義

評価

コンパイラは各式または部分式に対して2種類の評価を実行します(いずれもオプションです):

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

式が副作用を生じず、かつコンパイラが値が使用されないと判断できる場合、その式は 評価されない可能性があります

順序付け

Sequenced-before は、同一スレッド内の評価間における非対称的、推移的、ペアワイズの関係です(アトミック型とメモリバリアが関与する場合はスレッドをまたがる可能性があります)。

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

ルール

1) すべての関数引数と関数指定子の評価の後、実際の関数呼び出しの前に、シーケンスポイントが存在します。
2) 以下の二項演算子の最初(左)のオペランドの評価後、2番目(右)のオペランドの評価前にシーケンスポイントが存在します: && (論理AND)、 || (論理OR)、および , (コンマ演算子)。
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 behavior
3) 少なくとも1つの許容される部分式の順序付けがそのような非順序付けされた副作用を許可する限り、上記の規則が適用されます。

関連項目

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

C++ ドキュメント for 評価順序