C言語のきほん|複合代入演算子のしくみ

+= は「足して保存」を一発でやる近道!複合代入を覚えると、更新処理がスッキリ気持ちよく書けるよ。

C言語を書いていると、変数の値を少しずつ増やしたり減らしたり、倍率をかけたり…みたいな「更新」がとてもよく出てきます。
そのたびに score = score + 10 のように書いてもいいんですが、同じ変数名が何度も出てくると、ちょっと読みにくく感じることがあります。

そこで活躍するのが 複合代入演算子 です。
score += 10 のように書けて、意味は「score に 10 を足して、結果を score に保存する」。
つまり 計算と代入(保存)をセットで書ける便利な書き方なんですね。

この記事では、複合代入演算子が「結局どんな動きをしているのか」を、式の対応関係や具体例でしっかり理解できるように解説していきます。

複合代入演算子の基本形式

基本はこの対応関係です。

変数 = 変数 + 値;
変数 += 値;

複合代入演算子は「同じ変数を更新する」ための省略形、と考えるとスッと入ります。

どうして読みやすくなるの?

同じ変数名を2回書かなくていいので、更新の意図が前に出ます。

  • total = total + price; だと、目が行ったり来たりしやすい
  • total += price; だと、「total を増やすんだな」が一発で分かる

主な複合代入演算子の一覧

算術系の複合代入はこのあたりが定番です。
書くときは、記号の間にスペースを入れないのがルールです(+ = は別物になってしまいます)。

演算子意味例の意味(展開)
+=加算して代入a += 5;a = a + 5;
-=減算して代入a -= 3;a = a - 3;
*=乗算して代入a *= 2;a = a * 2;
/=除算して代入a /= 4;a = a / 4;
%=剰余して代入a %= 3;a = a % 3;

サンプルプログラム

ファイル名:6_5_1.c

#include <stdio.h>

int main(void)
{
    int stamina = 10;   /* 体力 */
    int steps = 0;      /* 歩数 */
    int coins = 10;     /* コイン */
    int level = 1;      /* レベル */
    int bonus = 10;     /* ボーナス値(確認用) */

    /* 複数の変数に同じ値を入れる(右から左へ代入される) */
    steps = coins = bonus = 10;

    stamina += 5;       /* 体力を5増やす */
    coins -= 3;         /* コインを3減らす */
    level *= 2;         /* レベルを2倍にする */
    bonus /= 4;         /* ボーナスを4で割る(整数除算) */
    steps %= 3;         /* 歩数を3で割った余りにする */

    printf("体力は%dになりました。\n", stamina);
    printf("コインは%d枚になりました。\n", coins);
    printf("レベルは%dになりました。\n", level);
    printf("ボーナスは%dになりました。\n", bonus);
    printf("歩数の余りは%dになりました。\n", steps);

    printf("メッセージ: 更新処理は複合代入でスッキリ書けます。\n");

    return 0;
}

このプログラムで見てほしいポイント

行の意図何が起きている?
stamina += 5;stamina = stamina + 5; と同じ
coins -= 3;coins = coins - 3; と同じ
level *= 2;level = level * 2; と同じ
bonus /= 4;bonus = bonus / 4; と同じ(整数除算なので小数は切り捨て)
steps %= 3;steps = steps % 3; と同じ(余りを保存)

特に /= は「割った結果を保存」ですが、int の場合は小数点以下が消えます。ここは前の「計算の落とし穴」とつながる注意点ですね。

しくみをもう一段だけ深く(評価順と読み方)

複合代入は、気持ちとしてはこうです。

1) 変数の今の値を取り出す
2) 右側の値を使って計算する
3) 計算結果を同じ変数に上書きする

式として「展開」すると分かりやすいので、迷ったら一度 a = a + 5 の形に戻して考えるのがコツです。

実践問題

次の料金で、映画館で買い物をしました。支払金額を計算するプログラムを作成してください。

  • チケット 1800円 を 1枚
  • ポップコーン 420円 を 1個
  • ドリンク 280円 を 1個
  • 会員割引があるので、合計金額から 150円 引き
  • 消費税率は 10% とします
  • 計算の途中で、複合代入演算子を使えるところでは使ってください

出力は次の3行を表示してください。

  • 合計金額
  • 割引後の金額
  • 税込み金額

実行例(イメージ)

合計金額 = 2500円
割引後の金額 = 2350円
税込み金額 = 2585円

解答例

ファイル名:6_5_2.c

#include <stdio.h>

int main(void)
{
    int total = 0;          /* 合計金額 */
    int discounted;         /* 割引後 */
    int taxed;              /* 税込み */
    int coupon = 150;       /* 割引額 */
    int tax_rate = 10;      /* 税率(%) */

    /* 商品を合計に足していく */
    total += 1800;  /* チケット */
    total += 420;   /* ポップコーン */
    total += 280;   /* ドリンク */

    printf("合計金額 = %d円\n", total);

    discounted = total;
    discounted -= coupon;   /* 割引を適用 */

    printf("割引後の金額 = %d円\n", discounted);

    /* 税込み = 割引後 + (割引後 × 税率 / 100) */
    taxed = discounted;
    taxed += discounted * tax_rate / 100;

    printf("税込み金額 = %d円\n", taxed);

    return 0;
}

解説(どこで複合代入を使っている?)

  • total += 1800 のように、合計に「足し込む」処理は複合代入が最強です
  • discounted -= coupon で、割引の「引き算更新」が読みやすくなります
  • taxed += discounted * tax_rate / 100 で、税額を「足す」更新にできます

ここで tax の計算が整数なのは、円を整数で扱っているからです。お金は整数で扱うと、浮動小数点の誤差を避けやすくて安心です。

実践問題(お釣りと最小枚数)

3つの商品を購入しました。それぞれの値段は 980円、1540円、365円 です。
この合計金額に対して 5000円札で支払います。お釣りを計算し、さらにそのお釣りをできるだけ少ない枚数の紙幣・硬貨で受け取るための枚数を求めてください。

使用する紙幣・硬貨
1000円札、500円玉、100円玉、50円玉、10円玉、5円玉、1円玉

条件

  • 複合代入演算子が使えるところでは、必ず複合代入演算子を使用してください
  • 計算は大きい単位から小さい単位へ順番に行ってください

実行結果例(イメージ)

合計金額 = 2885円
お釣り = 2115円

お釣りの枚数
1000円札の枚数 = 2枚
500円玉の枚数 = 0枚
100円玉の枚数 = 1枚
50円玉の枚数 = 0枚
10円玉の枚数 = 1枚
5円玉の枚数 = 1枚
1円玉の枚数 = 0枚

解答例

ファイル名:6_5_3.c

#include <stdio.h>

int main(void)
{
    int price1 = 980;
    int price2 = 1540;
    int price3 = 365;

    int total = 0;          /* 合計金額 */
    int pay = 5000;         /* 支払金額 */
    int change;             /* お釣り */

    int c1000, c500, c100, c50, c10, c5, c1;

    /* 合計金額を計算(複合代入で足し込む) */
    total += price1;
    total += price2;
    total += price3;

    printf("合計金額 = %d円\n", total);

    /* お釣りを計算 */
    change = pay;
    change -= total;

    printf("お釣り = %d円\n", change);

    /* 大きい単位から枚数を求め、残り金額を減らす */
    c1000 = change / 1000;
    change %= 1000;

    c500 = change / 500;
    change %= 500;

    c100 = change / 100;
    change %= 100;

    c50 = change / 50;
    change %= 50;

    c10 = change / 10;
    change %= 10;

    c5 = change / 5;
    change %= 5;

    c1 = change;   /* 最後に残った金額が1円玉の枚数 */

    printf("\nお釣りの枚数\n");
    printf("1000円札の枚数 = %d枚\n", c1000);
    printf("500円玉の枚数 = %d枚\n", c500);
    printf("100円玉の枚数 = %d枚\n", c100);
    printf("50円玉の枚数 = %d枚\n", c50);
    printf("10円玉の枚数 = %d枚\n", c10);
    printf("5円玉の枚数 = %d枚\n", c5);
    printf("1円玉の枚数 = %d枚\n", c1);

    return 0;
}

解説(複合代入が効いているところ)

この問題のキモは、残り金額 change を「更新しながら」枚数を決めていくところです。

  • change %= 1000 は、change = change % 1000 の省略形
    つまり「1000円札で取った残りだけに更新する」という意味になります。

ここを流れ図で表すとこんな感じです。

change(お釣り)
  ↓ 1000円札の枚数 = change / 1000
change を change % 1000 に更新(残りへ)
  ↓ 500円玉の枚数 = change / 500
change を change % 500 に更新
  ↓ ...
最後に残った change が 1円玉

ちょっと注意(複合代入の割り算と余り)

書き方意味注意点
a /= 4;a を 4 で割って更新a が int なら小数は切り捨て
a %= 3;a を 3 で割った余りに更新a が負数のときの余りの扱いは処理系差が出ることがあるので、教材ではまず正の値で練習すると安心