C言語基礎|演算子の優先順位と結合性

カッコなしで書いた式、あなたの想像どおりに動いてる?――Cの優先順位と結合性を味方にしよう。

式が長くなるほど「読み違い」がバグになる

C言語は演算子が豊富で、式を短く書けるのが魅力です。
でもその分、カッコを省略した式は「どこから計算されるか」を読み違えやすくて、地味〜に痛いバグの原因になります。

ここで押さえたいのは2つだけ。

  • 優先順位:どの演算子が先に計算されるか
  • 結合性:同じ優先順位が並んだとき、左から結ぶか右から結ぶか

この2つがわかると、式の意味がスッと読めるようになりますよ。

優先順位ってなに?

優先順位は「どれが先に計算されるか」のルールです。
身近な数学と同じで、加算より乗算が先、みたいな感じですね。

優先順位のイメージ

説明
見た目で左から順に計算しているわけじゃなく、優先順位が高い演算子(この例では *)が先に評価されます。

結合性ってなに?

結合性は「同じ優先順位の演算子が続いたとき、どっちにまとまるか」のルールです。

左結合と右結合

図の説明
例えば - は左結合なので、5 - 3 - 1 は (5 - 3) - 1 になります。
一方、代入 = は右結合なので、a = b = 1 は a = (b = 1) になります。

優先順位の“実務用”詳細表(よく使うものを中心に)

上ほど優先順位が高いです。
「形式」欄は見た目のパターンをそのまま書いてあります。

よく使う演算子中心・優先順位と結合性

優先順位演算子(代表)形式例何をする?(ざっくり)結合性実務メモ(落とし穴)
1関数呼び出しf(x)関数を呼ぶ引数の中はさらに優先順位が効く
添字a[i]配列アクセスi++ を入れると読みづらい
メンバ参照s.m構造体メンバ. は強い
ポインタメンバp->mポインタ先メンバ-> も強い
後置増減x++ / x--使った後に増減値としては「増える前」が使われる
2前置増減++x / --x先に増減代入と絡めると事故るので単独推奨
単項 + -+x / -x符号-10 は「定数」ではなく「10に単項-」
論理否定!x0なら1、非0なら0真偽値に寄せる
ビット反転~x全ビット反転符号付きに対する意味を意識
アドレス&xアドレス取得単項 & と ビットAND & は別物
間接参照*pポインタ参照単項 * と 乗算 * は別物
sizeofsizeof x / sizeof(型)サイズ取得sizeof(式)は式を評価しない(※VLA除く)
キャスト(型)x型変換強いので影響範囲をカッコで明示すると良い
3乗除剰余x*y / x/y / x%y算術整数除算は小数切り捨て
4加減x+y / x-y算術ここは直感通り
5シフトx<<n / x>>nビット移動加減より弱いので x<<1+2 は要注意
6関係(大小)< <= > >=比較連鎖比較 x<y<z は意図どおりにならない
7等価== !=等しい?< より弱い(ここが直感とズレやすい)
8ビットANDx & yビット論理積== より弱いので x & y == 0 は危険
9ビットXORx ^ yビット排他フラグ反転などに使う
10ビットORx | yビット論理和フラグセットに使う
11論理ANDx && y真偽のAND短絡評価あり(左が偽なら右を見ない)
12論理ORx || y真偽のOR短絡評価あり(左が真なら右を見ない)
13条件c ? a : b三項演算ネストは読みにくいのでカッコ推奨
14代入= += -= …代入a=b=1 は a=(b=1)
15コンマx, y順に評価して最後を返すfor の中以外は避けるのが無難

この表の使い方
「どっちが先?」と思ったら、表で上にある演算子が先です。
「同じ段に並んでたら?」は、結合性で左右が決まります。

“混乱しやすい組み合わせ”早見表(ここが一番役に立つ)

実務でミスが多いのは、だいたいここです。

よくある落とし穴と安全な書き方

書き方実際の解釈ありがちな勘違い安全な書き方
x << 1 + 2x << (1 + 2)(x << 1) + 2(x << (1 + 2)) または ((x << 1) + 2)
a & b == 0a & (b == 0)(a & b) == 0((a & b) == 0)
x < y == z(x < y) == zx < (y == z)(x < y) && (y == z) など意図を分解
mask & 1 << nmask & (1 << n)(mask & 1) << n(mask & (1U << n))
!x & 1(!x) & 1!(x & 1)!(x & 1) と書く
a && b真偽でANDa & b と同じフラグなら &、条件なら && と使い分け
a || b真偽でORa | b と同じ条件なら ||、ビットなら |

表の説明

  • == は & より強いので、ビット判定はカッコ必須級
  • << は + より弱いので、シフト量に式を入れるときは特に注意
  • 論理演算子とビット演算子は目的が違う(値の扱いが根本的に違う)

結合性で読みが変わる例

同じ優先順位が並ぶときの結び方

演算子の例結合性実際のまとまり
- / % << >> < <= > >= == != & ^ | && ||左結合5 - 3 - 1(5 - 3) - 1
= や += など右結合a = b = 7a = (b = 7)
?:右結合a ? b : c ? d : ea ? b : (c ? d : e)

表の説明
代入が右結合なのは、連鎖代入が書ける理由そのものです。
条件演算子 ?: も右結合なので、入れ子になるときはカッコで読みやすくするのが親切です。

サンプルプログラム

ここでは「優先順位と結合性で結果が変わる」ことを、短いプログラムで体感します。

プロジェクト名:chap7-27-1 ソースファイル名:chap7-27-1.c

// 優先順位と結合性の違いを確認する
#include <stdio.h>

int main(void)
{
    int a = 6;   // 110
    int b = 2;   // 010
    int x = 8;

    printf("優先順位と結合性の確認をします。\n\n");

    // 1) シフトと加算の優先順位
    printf("【例1】x << 1 + 2 と (x << 1) + 2 を比べます。\n");
    printf("x << 1 + 2       = %d\n", x << 1 + 2);
    printf("(x << 1) + 2     = %d\n\n", (x << 1) + 2);

    // 2) & と == の優先順位(落とし穴)
    printf("【例2】a & b == 0 と (a & b) == 0 を比べます。\n");
    printf("a & b == 0       = %d\n", a & b == 0);
    printf("(a & b) == 0     = %d\n\n", (a & b) == 0);

    // 3) 代入は右結合
    printf("【例3】代入は右結合なので、連鎖代入ができます。\n");
    int p, q;
    p = q = 7;
    printf("p = %d, q = %d\n", p, q);

    return 0;
}

このプログラムで見えること

  • x << 1 + 2 は x << (1 + 2) と同じ解釈になりやすい
  • a & b == 0 は (a & b) == 0 ではない
  • p = q = 7 が動くのは、= が右結合だから

登場する命令(構文)と何をする命令か

printf 関数

書式

  • printf(書式文字列, 引数1, 引数2, ...);

何をする命令?
標準出力(通常は画面)に文字や数値を表示します。今回のように、式の評価結果を確認するのに便利です。

代入演算子 =

書式

  • 左辺 = 右辺

何をする命令?
右辺の値を左辺(変数など)に格納します。右結合なので a = b = 1 のように連鎖ができます。

シフト演算子 << >>

書式

  • a << b
  • a >> b

何をする命令?
整数のビット列を左右にずらします。優先順位は + - より低いので、x << 1 + 2 が x << (1 + 2) になりやすい点が重要です。

ビットAND &

書式

  • a & b

何をする命令?
a と b をビットごとにANDします。条件判定で使うときは (a & mask) == 0 のようにカッコを付けるのが安全です。

等価演算子 ==

書式

  • a == b

何をする命令?
a と b が等しいなら 1、違うなら 0 になります。& より優先順位が高いので、a & b == 0 は要注意です。

実務での安全ルール(読みやすさ優先でOK)

迷ったらこれ

目的推奨
比較やビット判定を混ぜる必ずカッコを付ける(例:(x & mask) != 0)
シフトと加算を混ぜるカッコで意図を固定する(例:x << (n + 1))
条件演算子 ?: が長いカッコか if 文に分解する
可読性が最優先優先順位に頼らず、カッコで明示する

表の説明
コンパイラは優先順位どおりに解釈してくれますが、人間は疲れると読み違えます。
バグを減らすなら「正しさ+読みやすさ」の両方を狙うのがいちばんです。