Contents
演算子優先順位の全体像
C 言語では 「数値が小さいほど高い優先度」 として規格上で定義されています(※実装側はこの相対関係だけを保証し、数値そのものは任意です)。
同じ優先度に属する演算子は 結合規則(Associativity) に従って評価順が決まります。
- 左結合 (left‑to‑right) : 同レベルの演算子が左から右へ結びつく
- 右結合 (right‑to‑left) : 同レベルの演算子が右側から先に評価される
- 非結合 (none) : 後置・前置演算子など、結合方向が定義されないもの
以下の表は規格で明示された 10 個の優先度階層(C11/C17 では同一)を、理解しやすいように「1 が最高」形式で数値化したものです。
公式表
| 優先度 | 演算子・構文例(左結合 / 右結合 / 非結合) | 説明 |
|---|---|---|
| 1 | () [] . -> |
後置演算子 – 関数呼び出し、配列添字、構造体・共用体メンバアクセス。 結合規則:左結合 |
| 2 | ++ (後置) -- (後置) (type){...} (複合リテラル) |
後置インクリメント/デクリメント、複合リテラル。 結合規則:左結合 |
| 3 | ++ (前置) -- (前置) + (単項正) - (単項負) ! (論理否定) ~ (ビット NOT) * (間接参照) & (アドレス演算子) sizeof _Alignof |
単項前置演算子。 結合規則:右結合 |
| 4 | * / % |
乗除算・剰余。 左結合 |
| 5 | + - |
加減算。 左結合 |
| 6 | << >> |
ビットシフト(左/右)。 左結合 |
| 7 | < <= > >= |
比較演算子 (関係)。 左結合 |
| 8 | == != |
等価比較。 左結合 |
| 9 | & (ビット AND) |
ビット積。 左結合 |
| 10 | ^ (ビット XOR) |
排他的論理和。 左結合 |
| 11 | | (ビット OR) |
ビット和。 左結合 |
| 12 | && |
論理 AND。 左結合 |
| 13 | || |
論理 OR。 左結合 |
| 14 | ?: (条件演算子) |
三項演算子。右側の : があるため 右結合(式全体は単一演算子として扱われる)。 |
| 15 | = += -= *= /= %= <<= >>= &= ^= |= |
代入演算子。 右結合 |
| 16 | , (カンマ) |
カンマ演算子(式の評価順序を明示)。 左結合 |
補足:_Atomic は「型指定子」
C11 で導入された _Atomic は 型修飾子 であり、演算子ではありません。そのため優先順位表に含める根拠はなく、本表から除外しています。実際の式中で atomic_int a = ATOMIC_VAR_INIT(0); のように使用されても、評価順序には影響しません(規格上は単なる型情報)。
他サイトとの比較と注意点
| 項目 | 本表 (公式) | Zenn (2025/03) | 主な相違点・留意点 |
|---|---|---|---|
| 優先度番号の付け方 | 1 が最高、16 が最低(Microsoft Learn と同一) | 「1 が最下位」方式を採用し、全体が逆転している | 数字は 相対関係 のみ意味がある。実務では「高い⇔低い」の概念で比較すれば問題なし |
条件演算子 ?: の位置 |
14(右結合) | 13 と表記(同様に右結合) | 表示上の番号違いだけで、評価規則は一致 |
| 前置インクリメント・デクリメントの分類 | 同一レベル (優先度 3) | Zenn は「前置 ++/--」を優先度 2 に分離している |
規格では 単項前置演算子 とみなすので、同レベルが正しい |
| カンマ演算子の位置 | 最低 (16) | 同様に最低 | 完全一致 |
結論:Zenn の表は見やすさを追求した独自の番号付けであり、相対的な優先度・結合規則は公式と同一です。混乱を防ぐためには「番号」ではなく「高い⇔低い」の概念で比較してください。
評価順序を確認する実例
| 式 | 優先度だけでの解釈 (高→低) | 実際の評価順序(結合規則含む) | 解説 |
|---|---|---|---|
a + b * c |
* が 4、+ が 5 → b*c が先に計算 |
b * c → a + (b*c)(左結合) |
典型的な「乗算が足し算より高優先度」 |
*p++ |
後置 ++(優先度 2)と間接参照 *(優先度 3) → p++ が先に評価 |
tmp = p; p = p + 1; result = *tmp; |
後置インクリメントが先に実行され、元のポインタ値がデリファレンスされる |
func(a, b ? c : d) |
カンマは最低 (16) → 関数呼び出し全体が一つの式として扱われる | 1️⃣ a と b ? c : d の評価順序は未規定(C11 6.5.2.2)2️⃣ 条件演算子は右結合で、先に b が評価され、次に c または d |
引数評価の副作用に注意。コンパイラが左→右・右→左どちらでも正しい |
x = y = z + 1 |
+ (5) → = (15) が低優先度、かつ右結合 |
temp = z + 1; y = temp; x = y; |
代入が右から左へ行われるので、最終的に x と y は同じ値になる |
sizeof *p + 1 |
sizeof (単項前置, 優先度 3) * (間接参照, 同優先度) → 結合規則は右結合なので sizeof (*p) が先に評価 |
size = sizeof(*p); result = size + 1; |
sizeof は式全体を評価しないので、ポインタが無効でも安全 |
ポイント:優先度だけで「どの演算子が早く実行されるか」は判定できても、結合規則(左/右) が入ると最終的な評価順序が決まります。式を手書きで分解するときは必ず両方を意識してください。
未定義動作・実装依存の落とし穴
1. 後置 ++/-- と間接参照 * の組み合わせ
|
1 2 3 4 5 |
int arr[3] = {10,20,30}; int *p = arr; int v1 = *p++; // 正しい:v1 == 10、p が次要素へ進む int v2 = *++p; // 正しい:p が先にインクリメントされ、v2 == 30 |
- 注意点:同一式内で
*と後置インクリメントが混在すると、評価順序は規格で決まっている(後置が先)。しかし 副作用が重なる 場合は可読性が低くバグの温床になるので、別行に分けることを推奨。
2. 符号付き右シフトの実装依存
|
1 2 3 4 |
int a = -8; // 0b11111000 (二補数表現) int b = a >> 1; // 実装により算術シフト(−4)か論理シフト(2147483644)になる unsigned int u = (unsigned)a >> 1; // 常に論理シフト → 1073741824 |
- 規格: C11 6.5.7/4 「右シフトの結果は実装依存」
- 安全策: 符号ビットが重要な場合は必ず
unsignedにキャストしてからシフト、または標準ライブラリ (uint32_t) を用いる。
3. 符号付き整数のオーバーフロー
|
1 2 3 |
int x = INT_MAX; x = x + 1; // 未定義動作 (C11 5.2.4/4) |
- 影響: コンパイラは「未定義」領域を最適化で削除でき、結果が全く予測不能になる。
- 回避法:
if (x < INT_MAX) ++x;あるいは広い型 (int64_t) に昇格させてから演算する。
4. カンマ演算子の意図しない評価順序
|
1 2 3 |
int i = 0; int a = (i++, i + 1); // i が 1 になる前に右側が評価される → a == 2 |
- ポイント: カンマは 左結合 であり、左側の式が完全に評価された後に右側へ進む。副作用がある場合は順序を明示的に書くべき。
実務向けチェックリスト & チートシート
1. 演算子優先順位チェックリスト
| # | 確認項目 | 詳細 |
|---|---|---|
| ① | 高優先度演算子が低優先度演算子に埋もれていないか | * / % が + - の前に来ているか |
| ② | 結合規則の正しい適用 | 同一レベルで左結合か右結合かを式ごとに手書きで確認 |
| ③ | 後置・前置インクリメント/デクリメントが *、[] と混在していないか |
例: *p++ は OK、*++p-- のような複合は可読性低 → 分割 |
| ④ | ビットシフトの符号付き使用 | 必要なら必ず unsigned にキャスト |
| ⑤ | 条件演算子 ?: 内で副作用が無いか |
例: a = (b++ ? c : d) は評価順序依存 → 別行に分割 |
| ⑥ | カンマ演算子の意図的使用 | カンマは「式列」の意味しか持たない。for 文以外での多用は避ける |
| ⑦ | 代入チェーンが右結合であることを踏まえているか | x = y = z; は y = z; x = y; と等価 |
2. 印刷可能チートシート(PDF)
- 入手先:Microsoft Learn の「Operator precedence」ページの右上にある Print friendly リンク[^1]。
- 推奨活用法
- デスクトップ横に貼って常時参照
- IDE のサイドバーに画像として保存し、コード補完と併せて閲覧
3. サンプルコード集(GitHub Gist)
|
1 2 3 4 5 6 7 8 9 10 |
/* 演算子優先順位デモ */ #include <stdio.h> int main(void) { int a = 2, b = 3, c = 4; printf("%d\n", a + b * c); // 14 printf("%d\n", (a + b) * c); // 20 printf("%d\n", *(&a + 1)); // 未定義(ポインタ算術外)←要注意 return 0; } |
- 備考:実行結果はコンパイラの最適化設定に左右されないよう、
volatileを付与すると安全です。
参考文献・出典
- Microsoft Learn – C language: Operator precedence
https://learn.microsoft.com/ja-jp/cpp/c-language/operator-precedence?view=msvc-170(閲覧日:2026‑04‑19) - ISO/IEC 9899:2011 (C11) – 第 6.5 節「式」および付属の優先順位表。
- ISO/IEC 9899:2018 (C17) – 同上、変更点は
_Atomicの型修飾子追加のみ(演算子優先度は不変)。 - Zenn記事 – 「C言語 演算子優先順位まとめ」(2025‑03) – 本稿で比較対象として使用。
- Sejuku・Tech Blog – 「C の未定義動作まとめ」(2023) – 未定義・実装依存例の解説に参照。
まとめ
- C の演算子優先順位は 10 段階の相対関係 と 結合規則 によって決まります。
_Atomicは型修飾子であり、優先順位表に含めるべきではありません。- 他媒体(Zenn 等)の番号付けは見やすさ重視の独自表記であり、相対的な高低と結合規則が公式と一致しているか を確認すれば問題ありません。
- 実務では「高優先度 ⇔ 低優先度」の概念を頭に入れ、チェックリストとチートシートを活用することで、演算子関連バグの発生率を大幅に削減できます。
本稿は2026 年 4 月時点の最新情報に基づいています。規格改訂や実装変更があった場合は、公式ドキュメントをご確認ください。