C言語の基本|演算子の優先順位と結合規則

演算子の優先順位と結合規則を理解すれば、複雑な式も計算のまとまりごとに読み解けます。

C言語では、1つの式の中に+、*、==、&&など、複数の演算子が登場することがあります。

たとえば、次の式には+と*が含まれています。

result = 5 + 6 * 3;

この式では、左側にある+から処理するのではなく、優先順位の高い*による乗算が先にまとまりとして解釈されます。そのため、5 + 18となり、結果は23です。

複数の演算子を含む式を正しく読むために、C言語には次の2つの規則があります。

  • 優先順位は、異なる種類の演算子が並んだとき、どの演算子が強く結び付くかを決める
  • 結合規則は、同じ優先順位の演算子が並んだとき、左と右のどちらからまとまるかを決める

優先順位と結合規則を理解すると、式の意味を読み取りやすくなります。また、意図しない計算や条件判定によるバグも防ぎやすくなります。

なお、優先順位と結合規則が決めるのは、主に式のまとまり方です。関数の呼び出しや各オペランドが実際に評価される順序とは別の規則なので、まずは式がどのように区切られるかに注目して学んでいきましょう。

演算子の優先順位とは

演算子の優先順位とは、1つの式に異なる種類の演算子が含まれているとき、どの演算子が強く結び付くのかを決める規則です。

基本的な考え方は算数と同じです。乗算と除算は、加算と減算よりも優先順位が高くなっています。

次の式を見てみましょう。

int result = 5 + 6 * 3;

*は+より優先順位が高いため、式は次のようにまとまります。

5 + (6 * 3)
5 + 18
23

左側に+が書かれていても、必ず左から処理されるわけではありません。最初に演算子の優先順位を確認する必要があります。

乗算が加算より先にまとまることを確認するプログラム

ファイル名:6_10_1.c

#include <stdio.h>

int main(void)
{
    int fixed_fee = 5;
    int unit_price = 6;
    int quantity = 3;
    int total;

    total = fixed_fee + unit_price * quantity;

    printf("合計金額は%dです。\n", total);
    printf("乗算が加算より先にまとまります。\n");

    return 0;
}
実行結果の例
合計金額は23です。
乗算が加算より先にまとまります。

totalの計算式は、次のように記述されています。

total = fixed_fee + unit_price * quantity;

各変数の値を当てはめると、次の式になります。

5 + 6 * 3

*は+より優先順位が高いため、unit_price * quantityが先にまとまります。

5 + (6 * 3)
5 + 18
23

もしfixed_feeとunit_priceを先に加算したい場合は、丸括弧を使って計算のまとまりを変更します。

total = (fixed_fee + unit_price) * quantity;

この場合は、次のように計算されます。

(5 + 6) * 3
11 * 3
33

丸括弧の有無によって、23と33という異なる結果になります。数式をプログラムへ書き換えるときは、どの部分を先に計算したいのかを確認することが大切です。

図1:加算と乗算の優先順位

この図から分かること

5 + 6 * 3では、優先順位の高い乗算が先にまとまるため、結果は23になります。

加算を先に行いたい場合は、(5 + 6) * 3のように丸括弧を付けます。すると、丸括弧内の加算が先にまとまり、結果は33になります。

丸括弧を使うことで、通常の優先順位とは異なる式のまとまりを明確に指定できます。

演算子の結合規則とは

結合規則とは、同じ優先順位を持つ演算子が続けて並んだとき、式が左側と右側のどちらからまとまるのかを決める規則です。

結合規則には、主に次の2種類があります。

結合規則式のまとまり方主な演算子
左結合左から右へまとまる*、/、%、+、-、<、==など
右結合右から左へまとまる単項演算子、代入演算子など

多くの二項演算子は左結合です。一方、代入演算子は右結合です。

優先順位は異なる種類の演算子の強さを比べる規則です。結合規則は、同じ優先順位の演算子が並んだときに使われます。

加算と減算は左結合

二項演算子の+と-は同じ優先順位で、どちらも左結合です。

次の式を見てみましょう。

rest = stock - shipped + returned;

-と+は同じ優先順位なので、左結合によって次のようにまとまります。

(stock - shipped) + returned

stockが30、shippedが8、returnedが4の場合は、次の結果になります。

(30 - 8) + 4
22 + 4
26

右側のshipped + returnedが先にまとまるわけではありません。

在庫数の増減を左から計算するプログラム

ファイル名:6_10_2.c

#include <stdio.h>

int main(void)
{
    int stock = 30;
    int shipped = 8;
    int returned = 4;
    int rest;

    rest = stock - shipped + returned;

    printf("現在の在庫は%d個です。\n", rest);
    printf("同じ優先順位の加減算は左からまとまります。\n");

    return 0;
}
実行結果の例
現在の在庫は26個です。
同じ優先順位の加減算は左からまとまります。

restの計算式は、次のように記述されています。

rest = stock - shipped + returned;

この式では、-と+の優先順位が同じです。どちらも左結合なので、stock - shippedが先にまとまります。

(stock - shipped) + returned
(30 - 8) + 4
26

計算式を次のように変更すると、丸括弧によってまとまり方が変わります。

rest = stock - (shipped + returned);

この場合は、shipped + returnedが1つのまとまりになります。

30 - (8 + 4)
30 - 12
18

同じ値と演算子を使っていても、丸括弧の位置によって26と18という異なる結果になります。

代入演算子は右結合

代入演算子=は右結合です。そのため、代入演算子が連続している場合は、右側からまとまります。

次の式を見てみましょう。

first = second = third = 9;

この式は、次のように解釈されます。

first = (second = (third = 9))

代入の流れは次のとおりです。

thirdへ9を代入する
↓
secondへ9を代入する
↓
firstへ9を代入する

C言語の代入式は、代入した値を式の値として持ちます。そのため、third = 9の値をsecondへ代入し、さらにその値をfirstへ代入できます。

複数の変数へ同じ値を代入するプログラム

ファイル名:6_10_3.c

#include <stdio.h>

int main(void)
{
    int first;
    int second;
    int third;

    first = second = third = 9;

    printf("first=%d\n", first);
    printf("second=%d\n", second);
    printf("third=%d\n", third);
    printf("代入演算子は右からまとまります。\n");

    return 0;
}
実行結果の例
first=9
second=9
third=9
代入演算子は右からまとまります。

代入演算子=は右結合なので、最も右側にあるthird = 9が内側のまとまりになります。

first = (second = (third = 9))

最初にthirdへ9が代入されます。third = 9という代入式の値も9なので、その値がsecondへ代入されます。最後にfirstにも9が代入されます。

この書き方を使うと、複数の変数へ同じ初期値を代入できます。ただし、変数の型が異なる場合は、代入の途中で型変換が発生する可能性があるため、値の変化に注意が必要です。

図2:左結合と右結合の違い

この図から分かること

加算と減算は左結合なので、30 - 8 + 4は(30 - 8) + 4というまとまりになります。

一方、代入演算子は右結合です。first = second = third = 9は、最も右側にあるthirdへの代入から外側へまとまります。

結合規則は、同じ優先順位の演算子が続いているときに、式をどのように区切るか判断するための規則です。

よく使う演算子の優先順位

C言語には多くの演算子がありますが、最初からすべてを暗記する必要はありません。まずは、計算や条件式でよく使用する演算子の位置関係を確認しましょう。

次の表では、上にあるものほど優先順位が高くなっています。

優先順位種類演算子や書き方使用例
高い丸括弧によるグループ化()(a + b) * c
単項演算子やキャスト++、--、!、(型)++count、!flag、(double)a
乗算・除算・剰余*、/、%a * b、a / b、a % b
加算・減算+、-a + b、a - b
大小比較<、<=、>、>=score >= 0
等価比較==、!=number == 10
論理AND&&condition1 && condition2
論理OR||condition1 || condition2
代入=、+=、-=などtotal = 10
低いコンマ,a = 1, b = 2

丸括弧は通常の優先順位よりも強く式をグループ化するため、丸括弧内が1つのまとまりになります。

単項演算子は1つの値に作用します。たとえば、!flagはflagの真偽を反転し、(double)aはaをdouble型へ変換します。

*、/、%は、+や-より優先順位が高くなっています。そのため、a + b * cはa + (b * c)というまとまりになります。

大小比較は等価比較より優先順位が高く、等価比較は論理ANDより優先順位が高くなっています。条件式に複数の演算子が含まれる場合は、この位置関係を確認します。

代入演算子は、多くの計算演算子や比較演算子よりも優先順位が低くなっています。そのため、結果を計算してから変数へ代入する形になります。

比較演算子と論理演算子が混ざる式

点数が0以上100以下であるかを調べる場合は、次のような条件式を使用します。

(score >= 0) && (score <= 100)

=と<=は、&&より優先順位が高い演算子です。そのため、丸括弧を省略して次のように記述しても、同じまとまりになります。

score >= 0 && score <= 100

優先順位に従うと、次のように解釈されます。

(score >= 0) && (score <= 100)

ただし、条件式では丸括弧を付けたほうが、2つの比較結果を&&で結んでいることが分かりやすくなります。

優先順位を知っている場合でも、読み手が式の意味をすぐに理解できるように書くことが大切です。

代入演算子と等価演算子を混同しない

C言語では、代入演算子=と等価演算子==の役割が異なります。

演算子役割使用例
=右側の値を左側の変数へ代入するnumber = 1
==左右の値が等しいか比較するnumber == 1

if文の条件式で次のように記述すると、比較ではなく代入になります。

if (number = 1)

この式では、numberへ1が代入されます。さらに、代入式の値も1になります。C言語では0以外の値が真として扱われるため、この条件は真になります。

値が1と等しいかを比較したい場合は、==を使用します。

if (number == 1)

=と==の違いは、優先順位や結合規則だけでは防げない間違いです。if文やwhile文の条件式を書いたときは、比較のつもりで代入演算子を使っていないか確認しましょう。

減算が連続する場合も左結合になる

同じ優先順位の減算演算子が連続している場合も、左結合によって左側からまとまります。

次の式を見てみましょう。

result = 25 - 6 - 3;

この式は、次のように解釈されます。

result = (25 - 6) - 3;

最初に25 - 6を計算し、その結果から3を引きます。

連続する減算のまとまり方を確認するプログラム

ファイル名:6_10_4.c

#include <stdio.h>

int main(void)
{
    int result;

    result = 25 - 6 - 3;

    printf("result=%d\n", result);
    printf("計算のまとまりは(25 - 6) - 3です。\n");

    return 0;
}
実行結果の例
result=16
計算のまとまりは(25 - 6) - 3です。

-は左結合なので、25 - 6 - 3は次のようにまとまります。

(25 - 6) - 3
19 - 3
16

次のように、右側の減算へ丸括弧を付けると結果が変わります。

result = 25 - (6 - 3);

この場合は、丸括弧内の6 - 3が先にまとまります。

25 - (6 - 3)
25 - 3
22

通常の左結合では16、右側へ丸括弧を付けた場合は22になります。特に減算と除算は、まとまり方を変更すると結果が変わりやすいため注意が必要です。

丸括弧を使って式の意図を明確にする

優先順位と結合規則を理解することは大切ですが、すべての式を規則だけに頼って書く必要はありません。

式が長い場合や、異なる種類の演算子が混ざっている場合は、丸括弧を使ってまとまりを明確にできます。

丸括弧を使うときは、次の点を意識しましょう。

  • 優先して計算したい部分を丸括弧で囲む
  • 比較条件ごとに丸括弧を付ける
  • 読み手が計算順序を迷いそうな部分を明示する
  • 丸括弧を付けた後も、対応関係が正しいか確認する
  • 必要以上に丸括弧を重ねて、式を読みにくくしない

たとえば、a + b * cは優先順位によってa + (b * c)と解釈されます。

a + b * c

乗算を先に行う意図を明確にしたい場合は、次のように書けます。

a + (b * c)

加算を先に行いたい場合は、丸括弧の位置を変更します。

(a + b) * c

丸括弧は、単に計算結果を変えるためだけのものではありません。通常の優先順位と同じまとまりであっても、式を読みやすくする目的で付けることができます。

図3:丸括弧で計算の意図を固定する

この図から分かること

a + b * cでは、乗算の優先順位が高いため、a + (b * c)というまとまりになります。

(a + b) * cでは、丸括弧によって加算が先のまとまりになります。丸括弧の位置を変えると、式の意味や計算結果も変わります。

比較演算子と論理演算子が混ざる条件式では、それぞれの比較を丸括弧で囲むと、条件の構造が分かりやすくなります。優先順位だけに頼らず、読みやすさのために丸括弧を使うことも大切です。