Contents
1️⃣ はじめに
C 言語の式は「どの演算子が先に計算されるか」と「オペランドがいつ評価されるか」という二つの観点から理解する必要があります。特に大規模プロジェクトや組み込み系では、コンパイラ間で微妙に挙動が異なることがバグの温床になるため、公式ドキュメントと実装固有の情報を併せて把握しておくことが重要です。
本稿では Microsoft Visual C++ (MSVC) 170 が公開している演算子優先順位表をベースに、結合規則・評価順序・シーケンスポイントのポイントをまとめました。過度な「完全準拠」表現は避け、実際に確認できている範囲だけを書き記しています。
2️⃣ 演算子優先順位(MSVC 170)
| 優先度 (小さいほど高) | 演算子・例 | 結合規則 | 主な備考 |
|---|---|---|---|
| 1 | (), [], ., ->, ()(関数呼び出し) |
左結合 | 括弧・配列アクセス・メンバ参照は最優先 |
| 2 | !、~、+(単項+)-(単項‑)*(間接)、&(アドレス取得)、sizeof |
右結合 | 単項演算子は右から評価される |
| 3 | * / % |
左結合 | 乗除・剰余 |
| 4 | + - (二項) |
左結合 | 加減算 |
| 5 | << >> |
左結合 | ビットシフト |
| 6 | < <= > >= |
左結合 | 関係演算子 |
| 7 | == != |
左結合 | 等価比較 |
| 8 | & (ビット AND) |
左結合 | ビット単位の AND |
| 9 | ^ (ビット XOR) |
左結合 | ビット単位の XOR |
| 10 | | (ビット OR) |
左結合 | ビット単位の OR |
| 11 | && (論理 AND) |
左結合 | 短絡評価(左側が偽なら右側は評価されない) |
| 12 | || (論理 OR) |
左結合 | 短絡評価(左側が真なら右側は評価されない) |
| 13 | ?: (条件演算子) |
右結合 | 条件部は必ず先に評価 |
| 14 | =, +=, -=, *=, /=, %= , <<=, >>=, &=, ^=, |= |
右結合 | 代入系はすべて右結合 |
| 15 | , (カンマ) |
左結合 | 最低優先度、シーケンス点として機能 |
注: 表は「主要」な演算子だけを掲載しています。Microsoft Learn の公式ページには 全 25 段階が掲載されており、マクロや
_Genericなど特殊構文も含まれます(公式表へのリンク)。
3️⃣ 結合規則(Associativity)と実務的なポイント
| 規則 | 意味 | 典型例 |
|---|---|---|
| 左結合 (L) | 同一優先度の演算子が連続した場合、左側から右側へ木構造を組む。 | a - b - c → (a - b) - c |
| 右結合 (R) | 同一優先度の演算子が連続した場合、右側から左側へ木構造を組む。 | a = b = c → a = (b = c) |
実務ヒント
- 複雑な式は意図した結合順序がすぐに分かるよう、明示的に括弧を付けることを習慣化してください。
- 右結合の演算子(代入・?:)は「結果が左側へ流れる」イメージで覚えるとミスが減ります。
4️⃣ オペランドの評価順序(Order of Evaluation)
4.1 規格上の位置付け
C11/C18 では オペランドの評価順序は未定義 と明記されています。したがって、次のような式はコンパイラ依存です。
|
1 2 3 |
int i = 0; int r = i++ + ++i; // 未定義動作:左側・右側どちらが先に評価されるか不明 |
4.2 MSVC の実装傾向(注意喚起)
- 歴史的に MSVC は関数呼び出しの引数を左から右へ評価 してきました(例:
func(a(), b())でa()が先に呼ばれる)。この挙動は規格外であり、将来的に変更される可能性があります。 - 現在のドキュメントでも「実装上は左→右だが、標準では保証しない」旨が記載されています。そのため 副作用を伴う引数は必ず分割する ことが安全です。
|
1 2 3 4 5 |
/* 推奨:評価順序に依存しない書き方 */ int x = a(); int y = b(); printf("%d %d\n", x, y); |
4.3 シーケンスポイントとの関係
シーケンスポイント(;、&&、||、,、?: の左側など)では それ以前の副作用がすべて完了 すると規格で保証されています。したがって、次のようにシーケンスポイントを挟むだけで評価順序は確定します。
|
1 2 |
int a = ++i, b = i++; // カンマ演算子は左→右で評価され、シーケンスポイントになる |
5️⃣ 実務で役立つ「よくある落とし穴」
| 落とし穴 | 誤解例 | 正しい書き方 |
|---|---|---|
| ビット演算子と論理演算子の混同 | if (status & 0x01 && is_ready()) |
if ((status & 0x01) && is_ready()) または if ((status & 0x01) != 0 && is_ready()) |
| 関数引数の副作用 | printf("%d %d", i++, ++i); |
int a = i++; int b = ++i; printf("%d %d", a, b); |
| 条件演算子の右結合忘れ | x ? y : z ? w : v → (x ? y : z) ? w : v(意図と逆) |
x ? y : (z ? w : v) と明示的に括弧付け |
| カンマ演算子の評価順序誤認 | int r = (a++, b++); // 期待は a が先か? |
カンマ演算子は左→右で確定しているが、可読性のため別行に分割する |
ベストプラクティス
1. 明示的な括弧:優先順位・結合規則だけで判断せず、意図をコード上に残す。
2. 副作用はシーケンスポイントで区切る:;、カンマ、論理演算子の左側など。
3. コンパイラ固有情報はドキュメント化:MSVC の左→右評価傾向や GCC/Clang の未定義性をチーム内 Wiki にまとめる。
6️⃣ コンパイラ別実装差異と最新リソース
| コンパイラ | 評価順序の特徴 | 注意点 |
|---|---|---|
| MSVC (≥ 170) | 関数呼び出し引数は左→右に評価されることが多い(規格外) | 副作用を伴う引数は分割して書く |
| GCC / Clang | 引数評価順序は未定義(規格通り) | printf("%d %d", i++, ++i); は絶対に避ける |
| ICC | 同様に未定義だが、最適化レベルによって評価順序が変わることがある | デバッグビルドで動作確認を徹底 |
参考リンク(2026 年時点)
- Microsoft Learn – Precedence and Order of Evaluation
https://learn.microsoft.com/ja-jp/cpp/c-language/precedence-and-order-of-evaluation?view=msvc-170 - ISO C11 / C18 標準ドラフト(PDF)
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf (第5章 6.5式) - Zenn 記事 – 「C 言語演算子優先順位をわかりやすく」
https://zenn.dev/naoyoshinori/books/1b95e9e39c515f/viewer/3151c6 - Programming Place Plus – 「GCC と Clang の評価順序比較」
https://programmingplace.jp/articles/evaluation-order
7️⃣ 表の活用方法(Markdown / HTML)
- コピー&ペースト:本稿のテーブル部分をそのまま社内 Wiki や GitHub README に貼り付けるだけで利用可能。
- CSS カスタマイズ例
|
1 2 3 |
table { border-collapse: collapse; width: 100%; } th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } |
- PDF 化:ブラウザの「印刷 → PDF」機能で保存すれば、紙媒体でも配布しやすい。
ポイント:表は最新版(MSVC 170)に合わせて更新しておくと、コンパイラバージョンが上がった際にも情報の齟齬を防げます。
8️⃣ まとめ
- 演算子優先順位は MSVC 170 の公式表に従い 25 段階 に整理できる。
- 結合規則は左結合と右結合で覚え、明示的な括弧 が安全策になる。
- オペランドの評価順序は標準では未定義なので、シーケンスポイントで必ず区切る ことがベストプラクティス。
- MSVC は引数を左→右に評価する実装傾向があるが、規格外であり将来変更の可能性がある点に注意。
- コンパイラ別差異や最新リソースへのリンクを併記すれば、チーム全体で 「何が保証されていて何が未定義か」 を共有できる。
この表とポイントを手元に置くだけで、コードレビュー時の勘違いやバグ発生率を大幅に低減できます。ぜひ日常開発に取り入れてください。