C言語の基本|複合代入演算子の使い方

計算して、そのまま保存。複合代入演算子を使って、変数の更新を短く分かりやすく書こう。

C言語のプログラムでは、変数に入っている値を少しずつ更新する処理がよく登場します。

たとえば、次のような処理です。

  • 得点にボーナスを加える
  • 在庫数から出荷した個数を引く
  • 現在の値を2倍にする
  • 金額を一定の数で割る
  • 割り算の余りを新しい値として保存する

変数scoreに10を加えて更新する場合は、次のように書けます。

score = score + 10;

この書き方でも正しく動作しますが、同じ変数名が左辺と右辺の両方に書かれています。

そこで利用できるのが、複合代入演算子です。

score += 10;

この文は、scoreの現在値に10を加え、計算結果をscoreへ保存するという意味です。

複合代入演算子を使うと、計算と代入を1つの演算子で表せます。どの変数をどのように更新するのかが見えやすくなり、同じ変数名を繰り返し書く必要もありません。

ここでは、算術演算に関係する次の複合代入演算子を中心に解説します。

  • +=
  • -=
  • *=
  • /=
  • %=

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

複合代入演算子は、変数の現在値を使って計算し、その結果を同じ変数へ保存するときに使います。

基本的な対応関係は次のとおりです。

変数 = 変数 + 値;

この更新処理は、+=を使って次のように書けます。

変数 += 値;

たとえば、totalの現在値にpriceを加える場合は、次の2つの書き方ができます。

total = total + price;
total += price;

単純な変数を更新する場合、どちらもtotalの現在値にpriceを加え、その計算結果をtotalへ保存するという目的で使えます。

複合代入で行われる3つの処理

複合代入演算子による更新は、次の3段階で考えると分かりやすくなります。

  1. 左辺の変数から現在値を取り出す
  2. 右辺の値を使って計算する
  3. 計算結果を左辺の変数へ保存する

scoreの値が50のときに、次の文を実行したとします。

score += 10;

処理の流れは次のとおりです。

現在のscoreを取り出す:50
50に10を加える:60
計算結果をscoreへ保存する:scoreは60

以前の50は、計算結果の60で上書きされます。

図1:複合代入演算子で値を更新する仕組み

この図から分かること

score += 10;では、最初にscoreの現在値である50を取り出します。その値に10を加え、計算結果の60を同じscoreへ保存します。

複合代入演算子は、計算と代入をまとめて表す演算子です。

複合代入を使うと読みやすくなる理由

通常の代入を使った更新では、同じ変数名を2回書きます。

total = total + price;

複合代入演算子を使うと、変数名を繰り返さずに書けます。

total += price;

2つを比べると、複合代入ではtotalを増やす処理であることがすぐに分かります。

書き方読み方
total = total + price;totalとpriceを加算し、結果をtotalへ保存する
total += price;totalへpriceを加える

変数の現在値を更新するという意図が明確な場合は、複合代入演算子を使うとコードを読みやすくできます。

主な複合代入演算子

算術演算に関係する主な複合代入演算子は、次の5種類です。

演算子演算使用例展開した形
+=加算して代入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;

複合代入演算子は、2つの記号を続けて1つの演算子として記述します。

+=
-=
*=
/=
%=

記号の間に空白を入れて、+ =のようには書けません。+と=が別々の記号として扱われるため、複合代入演算子にはならないからです。

+= で加算して更新する

+= は、左辺の現在値に右辺の値を加え、結果を左辺へ保存します。

count += 5;

countの現在値が12の場合は、次のように処理されます。

12 + 5 = 17

更新後のcountは17です。

通常の代入で書くと、次の形になります。

count = count + 5;

+= は、次のような場面で利用できます。

  • 合計金額に商品の価格を足す
  • 得点にボーナスを加える
  • 処理件数を増やす
  • 移動距離を合計する

-= で減算して更新する

-= は、左辺の現在値から右辺の値を引き、結果を左辺へ保存します。

stock -= 4;

stockの現在値が15なら、更新後の値は11です。

15 - 4 = 11

通常の代入に展開すると、次のようになります。

stock = stock - 4;

-=は、在庫数の減少、残高からの支払い、体力の減少などを表すときに使えます。

*= で乗算して更新する

*= は、左辺の現在値と右辺の値を掛け、結果を左辺へ保存します。

value *= 3;

valueの現在値が6なら、更新後の値は18です。

6 × 3 = 18

通常の代入に展開すると、次の形です。

value = value * 3;

*=は、現在値を何倍かに増やす処理で利用できます。

/= で除算して更新する

/= は、左辺の現在値を右辺の値で割り、結果を左辺へ保存します。

value /= 4;

valueが20なら、更新後の値は5です。

20 ÷ 4 = 5

通常の代入に展開すると、次のようになります。

value = value / 4;

valueがint型の場合、整数同士の除算になります。

たとえば、valueが15の場合にvalue /= 4;を実行すると、15 / 4の小数部分は残りません。

数学上の結果:3.75
int型の結果:3

更新後のvalueには3が保存されます。

%= で余りに更新する

%= は、左辺の現在値を右辺の値で割り、その余りを左辺へ保存します。

value %= 4;

valueが15の場合、15を4で割った余りは3です。

15 ÷ 4
商:3
余り:3

更新後のvalueには3が保存されます。

通常の代入に展開すると、次の形です。

value = value % 4;

%=は整数型に対して使います。

5種類の複合代入をプログラムで確認しよう

次のプログラムでは、+=、-=、*=、/=、%=をそれぞれ使用して、変数の値を更新します。

5種類の複合代入演算子で値を更新するプログラム

ファイル名:6_5_1.c

#include <stdio.h>

int main(void)
{
    int energy = 20;
    int tasks = 0;
    int tokens = 15;
    int multiplier = 2;
    int adjustment = 15;

    /* 複数の変数へ同じ値を代入する */
    tasks = tokens = adjustment = 15;

    /* 複合代入演算子で値を更新する */
    energy += 8;
    tokens -= 4;
    multiplier *= 3;
    adjustment /= 4;
    tasks %= 4;

    printf("エネルギーは%dになりました。\n", energy);
    printf("トークンは%d個になりました。\n", tokens);
    printf("倍率は%d倍になりました。\n", multiplier);
    printf("調整値は%dになりました。\n", adjustment);
    printf("作業件数の余りは%dになりました。\n", tasks);

    return 0;
}
実行結果の例
エネルギーは28になりました。
トークンは11個になりました。
倍率は6倍になりました。
調整値は3になりました。
作業件数の余りは3になりました。

連続した代入の処理

プログラムでは、複数の変数へ同じ値を代入しています。

tasks = tokens = adjustment = 15;

代入演算子は右から左へまとまるため、次の順番で処理されます。

  1. adjustmentへ15を代入する
  2. tokensへ15を代入する
  3. tasksへ15を代入する

処理後は、3つの変数すべてが15になります。

tasksは15
tokensは15
adjustmentは15

+= によるenergyの更新

次の文では、energyの現在値に8を加えています。

energy += 8;

energyの初期値は20です。

20 + 8 = 28

したがって、更新後のenergyは28です。

この文を通常の代入に展開すると、次のようになります。

energy = energy + 8;

-= によるtokensの更新

tokensには、連続代入によって15が保存されています。

tokens -= 4;

現在値15から4を引くため、更新後の値は11です。

15 - 4 = 11

*= によるmultiplierの更新

multiplierの初期値は2です。

multiplier *= 3;

2と3を掛けた結果である6が、multiplierへ保存されます。

2 × 3 = 6

/= によるadjustmentの更新

adjustmentには15が保存されています。

adjustment /= 4;

adjustmentはint型なので、15 / 4は整数同士の除算です。

15 / 4 = 3

数学上は3.75ですが、小数部分は残りません。更新後のadjustmentは3です。

%=によるtasksの更新

tasksには15が保存されています。

tasks %= 4;

15を4で割った余りは3です。

15 % 4 = 3

更新後のtasksは3になります。

複合代入を読むときの順番

複合代入演算子の意味が分かりにくい場合は、通常の代入へ展開して考えると理解しやすくなります。

たとえば、次の文を見てみましょう。

balance -= fee;

通常の代入へ展開すると、次のようになります。

balance = balance - fee;

あとは、右辺から順番に処理を追います。

  1. balanceの現在値を取り出す
  2. feeの値を引く
  3. 計算結果をbalanceへ保存する

複合代入では、左辺が計算の対象であると同時に、計算結果の保存先にもなります。

実践問題:複合代入で税込み金額を求めよう

イベント会場で次の商品を購入したとします。

  • 入場券は2200円
  • 軽食は480円
  • ドリンクは320円
  • 会員割引は180円
  • 消費税率は10%

次の3つの金額を表示するプログラムを作成します。

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

合計金額には商品価格を順番に加えるため、+=を利用できます。

割引後の金額は、合計金額から割引額を引くため、-=を利用できます。

税込み金額は、割引後の金額に税額を加えるため、+=を利用できます。

商品価格の合計、割引、税額を複合代入で計算するプログラム

ファイル名:6_5_2.c

#include <stdio.h>

int main(void)
{
    int total = 0;
    int discounted;
    int taxed;
    int coupon = 180;
    int tax_rate = 10;

    /* 商品価格を合計へ加える */
    total += 2200;
    total += 480;
    total += 320;

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

    /* 合計金額をコピーして割引する */
    discounted = total;
    discounted -= coupon;

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

    /* 割引後の金額へ税額を加える */
    taxed = discounted;
    taxed += discounted * tax_rate / 100;

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

    return 0;
}
実行結果の例
合計金額 = 3000円
割引後の金額 = 2820円
税込み金額 = 3102円

+=で商品価格を合計する

変数totalは0で初期化されています。

int total = 0;

商品価格をtotalへ順番に加えます。

total += 2200;
total += 480;
total += 320;

値の変化は次のとおりです。

処理計算total
初期状態なし0
入場券を加える0 + 22002200
軽食を加える2200 + 4802680
ドリンクを加える2680 + 3203000

複数の値を合計へ足し込む処理では、+=を使うと更新の流れが分かりやすくなります。

-=で割引を適用する

合計金額を変数discountedへコピーします。

discounted = total;

この時点でdiscountedは3000です。

続いて、会員割引の180円を引きます。

discounted -= coupon;

計算は次のとおりです。

3000 - 180 = 2820

更新後のdiscountedは2820です。

+=で税額を加える

割引後の金額をtaxedへコピーします。

taxed = discounted;

次に、割引後の金額から税額を求め、taxedへ加えます。

taxed += discounted * tax_rate / 100;

税額は次のように計算されます。

2820 × 10 ÷ 100 = 282

現在のtaxedは2820なので、税額282を加えます。

2820 + 282 = 3102

最終的な税込み金額は3102円です。

図2:複合代入で合計・割引・税込み金額を更新する流れ

この図から分かること

商品の価格は、+=を使ってtotalへ順番に加えています。

割引では、discountedの現在値から180を引き、結果を同じdiscountedへ保存します。税額の計算でも、taxedの現在値へ税額を加えて更新しています。

複合代入演算子は、合計、割引、税額加算のように、現在の金額を段階的に更新する処理に向いています。

金額を整数で計算する理由

このプログラムでは、金額をint型の円単位で扱っています。

int total;
int discounted;
int taxed;

金額を整数で管理すると、浮動小数点数の内部表現によるわずかな誤差を避けやすくなります。

ただし、税額の計算も整数演算です。

discounted * tax_rate / 100

計算結果に小数部分が生じた場合、その小数部分は残りません。今回の2820 × 10 ÷ 100は282になるため、小数部分は発生しません。

実践問題:お釣りを最小枚数に分けよう

3つの商品を購入し、5000円で支払う場合のお釣りを計算します。

商品の価格は次のとおりです。

  • 1つ目の商品は1250円
  • 2つ目の商品は880円
  • 3つ目の商品は475円
  • 支払金額は5000円

まず、商品の合計金額を求めます。

1250 + 880 + 475 = 2605

お釣りは次のとおりです。

5000 - 2605 = 2395

この2395円を、次の紙幣と硬貨でできるだけ少ない枚数に分けます。

  • 1000円札
  • 500円玉
  • 100円玉
  • 50円玉
  • 10円玉
  • 5円玉
  • 1円玉

大きい単位から順番に枚数を求め、%=で残り金額を更新していきます。

お釣りを紙幣と硬貨の最小枚数に分けるプログラム

ファイル名:6_5_3.c

#include <stdio.h>

int main(void)
{
    int price1 = 1250;
    int price2 = 880;
    int price3 = 475;

    int total = 0;
    int payment = 5000;
    int change;

    int count1000;
    int count500;
    int count100;
    int count50;
    int count10;
    int count5;
    int count1;

    /* 商品価格を合計へ加える */
    total += price1;
    total += price2;
    total += price3;

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

    /* 支払金額から合計金額を引く */
    change = payment;
    change -= total;

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

    /* 大きい単位から枚数と残り金額を求める */
    count1000 = change / 1000;
    change %= 1000;

    count500 = change / 500;
    change %= 500;

    count100 = change / 100;
    change %= 100;

    count50 = change / 50;
    change %= 50;

    count10 = change / 10;
    change %= 10;

    count5 = change / 5;
    change %= 5;

    count1 = change;

    printf("\nお釣りの枚数\n");
    printf("1000円札の枚数 = %d枚\n", count1000);
    printf("500円玉の枚数 = %d枚\n", count500);
    printf("100円玉の枚数 = %d枚\n", count100);
    printf("50円玉の枚数 = %d枚\n", count50);
    printf("10円玉の枚数 = %d枚\n", count10);
    printf("5円玉の枚数 = %d枚\n", count5);
    printf("1円玉の枚数 = %d枚\n", count1);

    return 0;
}
実行結果の例
合計金額 = 2605円
お釣り = 2395円

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

商品の合計金額を求める

変数totalは0で初期化されています。

int total = 0;

3つの商品価格を+=で順番に加えます。

total += price1;
total += price2;
total += price3;

値の変化は次のとおりです。

0 + 1250 = 1250
1250 + 880 = 2130
2130 + 475 = 2605

最終的な合計金額は2605円です。

お釣りを-=で計算する

最初に、支払金額5000円をchangeへ代入します。

change = payment;

続いて、合計金額を引きます。

change -= total;

通常の代入に展開すると、次の形です。

change = change - total;

計算結果は2395円です。

5000 - 2605 = 2395

除算で紙幣と硬貨の枚数を求める

1000円札の枚数は、お釣りを1000で割って求めます。

count1000 = change / 1000;

changeは2395なので、整数除算の結果は2です。

2395 / 1000 = 2

1000円札を2枚使うと、2000円を受け取れます。

%=で残り金額へ更新する

1000円札の枚数を求めたあと、%=で残り金額を求めます。

change %= 1000;

通常の代入に展開すると、次のようになります。

change = change % 1000;

2395を1000で割った余りは395です。

2395 % 1000 = 395

changeは395へ更新されます。次の500円玉の枚数は、この395を使って計算します。

count500 = change / 500;

395 / 500の整数除算結果は0なので、500円玉は0枚です。

その後、change %= 500;を実行しても、395はそのまま残ります。

同じ処理を100円、50円、10円、5円の順番で繰り返します。

単位計算前のchange枚数更新後のchange
1000円23952395
500円3950395
100円395395
50円95145
10円4545
5円510
1円000

図3:%=で残りのお釣りを更新する流れ

この図から分かること

紙幣や硬貨の枚数は、changeをそれぞれの金額で割って求めます。

枚数を求めたあとは、change %= 金額;を実行し、changeを残り金額へ更新します。

この処理を大きい単位から小さい単位へ繰り返すことで、少ない枚数になるようにお釣りを分けられます。

/=と%=を使うときの注意点

/=と%=では、右辺の値を0にできません。

value /= 0;
value %= 0;

整数の除算や剰余で右辺が0になると、未定義の動作になります。

右辺に変数を使う場合は、必要に応じて0ではないことを先に確認します。

また、int型に対する/=では、小数部分が残りません。

int value = 15;
value /= 4;

更新後のvalueは3です。

%=を負の整数に使った場合、現在のC言語規格では余りの符号は左側のオペランドに従います。ただし、入門段階では動きを理解しやすい正の整数から練習するとよいでしょう。

複合代入演算子の読み方

複合代入演算子を見たときは、次のように読むと処理を理解しやすくなります。

読み方更新内容
value += 5;valueに5を加える加算結果をvalueへ保存
value -= 3;valueから3を引く減算結果をvalueへ保存
value *= 2;valueを2倍にする乗算結果をvalueへ保存
value /= 4;valueを4で割る除算結果をvalueへ保存
value %= 3;valueを3で割った余りにする剰余をvalueへ保存

意味が分かりにくいときは、通常の代入へ展開して考えます。

value += 5;
value = value + 5;

左辺の現在値を取り出し、右辺の値を使って計算し、結果を同じ左辺へ保存するという流れを意識すると、どの複合代入演算子でも読み解きやすくなります。