C言語の複合代入演算子(+=, -=, *=, /=)は、繰り返し登場する変数の更新をコンパクトに書ける便利な記法です。
カウンタや合計の更新、倍率の適用、平均の計算など、初学者が最初に出会う多くの場面で使われます。
本記事では、基本の意味から典型的な使い方、注意点、ベストプラクティスまでを丁寧に解説します。
代入演算子(+=, -=, *=, /=)の基本
代入演算子のメリット(短く書ける)
複合代入演算子は、左辺の変数を繰り返し書かずに更新できるのが最大の利点です。
たとえば合計値を増やす場面で、sum = sum + x
よりsum += x
の方が短くて読みやすく、タイプミスも減ります。
加えて、左辺の評価が1回で済むため、式に関数呼び出しや添字計算が含まれる場合に効率的です。
繰り返し変数名を書かないのでミスが減る
同じ変数名を2回書く必要がないため、たとえばsum = smu + x
のようなタイポを防げます。
コードレビューでも意図が読み取りやすくなります。
左辺の評価が1回で済む
a = a + b
は左辺a
を2回評価しますが、a += b
は1回だけです。
配列や関数呼び出しを含む左辺で特に有利です。
a += b は a = a + b と同じ
基本的な意味は「左辺に右辺を加えて、結果を左辺に戻す」です。
式としての結果(返り値)は代入後の左辺の値になります。
同等性の基本
多くのケースでa += b
はa = a + b
と等価に動作します。
どちらも足し算して代入という意図を持ちます。
評価回数の違いに注意
a = a + b
は左辺a
を2回参照するのに対し、a += b
は1回のみです。
a
がarr[f(i)]
のような式だと、計算コストや副作用が変わる可能性があります。
型変換のタイミング
複合代入では、結果が左辺の型に変換されてから代入されます。
たとえばint a = 1; a += 2.9;
は小数部が落ちて最終的にa == 3
になります。
詳細は後述の注意点でも扱います。
-=, *=, /= の意味と対応
-=
は引き算、*=
は掛け算、/=
は割り算をして、その結果を左辺に代入します。
次の表が対応関係です。
複合代入 | 同等の通常代入 | 例 |
---|---|---|
a += b | a = a + b | 合計やカウンタの増加 |
a -= b | a = a – b | 残高や差分の更新 |
a *= b | a = a * b | 倍率の適用や累積積 |
a /= b | a = a / b | 平均や割合の計算 |
よく使う場面(合計・カウンタ更新)
合計値sum
に値を加える、ループ内でカウンタcount
を1ずつ増やす、価格に税率を掛ける、合計を個数で割って平均に更新するなど、データ集計や繰り返し処理で頻出です。
代入演算子の使い方例
+= の使い方(合計・カウント)
配列の合計を計算し、80点以上の要素数を数える例です。
#include <stdio.h>
int main(void) {
int scores[] = {72, 88, 91, 65, 79};
int n = (int)(sizeof(scores) / sizeof(scores[0]));
int sum = 0;
int count80 = 0;
// 合計と80点以上のカウントを更新
for (int i = 0; i < n; ++i) {
sum += scores[i]; // sum = sum + scores[i] と同じ
if (scores[i] >= 80) {
count80 += 1; // 1ずつ増やす
}
}
printf("sum = %d\n", sum);
printf("count80 = %d\n", count80);
return 0;
}
sum = 395
count80 = 2
-= の使い方(残高・差分)
残高から引き出し額を順に引いていく処理や、差分の更新に使えます。
#include <stdio.h>
int main(void) {
int balance = 10000;
int withdraw[] = {1500, 2300, 1200};
int m = (int)(sizeof(withdraw) / sizeof(withdraw[0]));
for (int i = 0; i < m; ++i) {
balance -= withdraw[i]; // balance = balance - withdraw[i]
printf("after withdraw %d: balance = %d\n", i + 1, balance);
}
int x = 100, y = 30;
int diff = x;
diff -= y; // 差分を更新
printf("diff = %d (x - y)\n", diff);
return 0;
}
after withdraw 1: balance = 8500
after withdraw 2: balance = 6200
after withdraw 3: balance = 5000
diff = 70 (x - y)
*= の使い方(倍率・累積)
ポイントの2倍付与や、税率の適用、階乗などの累積積に使えます。
#include <stdio.h>
int main(void) {
int points = 120;
points *= 2; // 2倍にする
printf("points (double) = %d\n", points);
double price = 1200.0;
price *= 1.10; // 10%増し(税や手数料の例)
printf("price with 10%% up = %.2f\n", price);
int n = 5;
int fact = 1;
for (int i = 2; i <= n; ++i) {
fact *= i; // 階乗: 1*2*3*4*5
}
printf("5! = %d\n", fact);
return 0;
}
points (double) = 240
price with 10% up = 1320.00
5! = 120
/= の使い方(平均・割合)
合計を要素数で割って平均に更新する、割合に正規化する、といった用途で使います。
#include <stdio.h>
int main(void) {
double total = 17.0;
int count = 4;
// 平均を求める: total を平均に更新する
double avg = total; // total自体を残したいので別変数にコピー
avg /= count; // avg = avg / count
printf("avg = %.3f\n", avg);
// 割合(パーセンテージ)の例
double score = 42.0;
double max = 50.0;
score /= max; // 0.84 (84%) に正規化
printf("ratio = %.2f (%.0f%%)\n", score, score * 100.0);
return 0;
}
avg = 4.250
ratio = 0.84 (84%)
ループでの更新に便利(for/while)
ループ変数の増分や減衰処理も複合代入が読みやすいです。
#include <stdio.h>
int main(void) {
// forで2刻みのループ
for (int i = 0; i < 10; i += 2) { // i = i + 2
printf("i = %d ", i);
}
printf("\n");
// whileで値を半分ずつにしていく
int n = 100;
while (n > 1) {
n /= 2; // n = n / 2
printf("%d ", n);
}
printf("\n");
return 0;
}
i = 0 i = 2 i = 4 i = 6 i = 8
50 25 12 6 3 1
if で条件付き更新
条件を満たすときだけ増減したい場面でも、複合代入は明快です。
#include <stdio.h>
#include <stdbool.h>
int main(void) {
int stock = 8;
int reorderLevel = 10;
int reorderQty = 5;
if (stock < reorderLevel) {
stock += reorderQty; // 必要なときだけ補充
}
printf("stock = %d\n", stock);
int penalty = 3;
bool isLate = true;
if (isLate) {
penalty *= 2; // 期限遅れならペナルティを倍増
}
printf("penalty = %d\n", penalty);
return 0;
}
stock = 13
penalty = 6
代入演算子の注意点
整数の割り算は切り捨て(/=)
整数同士の割り算は小数点以下が切り捨てられます。
平均を求めるときは型に注意しましょう。
#include <stdio.h>
int main(void) {
int a = 5;
int b = 2;
int c = a;
c /= b; // 5 / 2 は 2 (小数切り捨て)
printf("int: 5/2 = %d\n", c);
double x = 5.0;
x /= b; // 5.0 / 2 は 2.5
printf("double: 5.0/2 = %.1f\n", x);
return 0;
}
int: 5/2 = 2
double: 5.0/2 = 2.5
0 で割らない(/=)
0で割ると未定義動作になり、クラッシュや不正な結果を招きます。
必ず事前チェックを行います。
#include <stdio.h>
int main(void) {
int total = 100;
int n = 0;
if (n != 0) {
total /= n;
printf("result = %d\n", total);
} else {
printf("error: divide by zero\n");
}
return 0;
}
error: divide by zero
int と double で結果が変わる
左辺の型が結果の型になります。
複合代入では右辺の計算結果が左辺の型に変換されて代入されるため、精度が落ちる場合があります。
#include <stdio.h>
int main(void) {
int a = 1;
a += 2.9; // 2.9 は int に変換され 2 となり、a は 3
printf("a (int) = %d\n", a);
double d = 1.0;
d += 2.9; // double のまま 3.9
printf("d (double) = %.1f\n", d);
return 0;
}
a (int) = 3
d (double) = 3.9
平均や割合を扱うときは、左辺をdoubleにするのが安全です。
必要に応じて(double)
でキャストしましょう。
大きすぎる値に注意(+=, *= のオーバーフロー)
intの範囲を超えると不正な結果になります。
特に符号付き整数のオーバーフローは未定義動作です。
安全に計算するには、範囲チェックやより広い型を使います。
#include <stdio.h>
#include <limits.h>
#include <stdbool.h>
bool will_add_overflow_int(int a, int b) {
if (b > 0) return a > INT_MAX - b;
if (b < 0) return a < INT_MIN - b;
return false;
}
int main(void) {
int a = 2000000000; // 20億
int b = 200000000; // 2億
if (will_add_overflow_int(a, b)) {
long long safe = (long long)a + (long long)b; // より広い型で計算
printf("use long long: %lld\n", safe);
} else {
a += b;
printf("a = %d\n", a);
}
// 乗算はより危険。広い型で先に計算し、範囲を確認する
int x = 50000, y = 50000;
long long prod = (long long)x * (long long)y;
if (prod > INT_MAX) {
printf("product out of int range: %lld\n", prod);
} else {
x *= y;
printf("x = %d\n", x);
}
return 0;
}
use long long: 2200000000
product out of int range: 2500000000
unsigned整数は桁あふれ時に2の補数でラップしますが、意図しない結果になりがちです。
基本はオーバーフローを避ける設計を優先してください。
a =+ b と a += b の違い(よくあるミス)
a =+ bはa = (+b)
という意味で、「bの正の値をaに代入」です。
a += b
と全く異なります。
#include <stdio.h>
int main(void) {
int a = 5, b = 3;
int x = 5, y = 3;
a =+ b; // a = (+b) と同義。a は 3 になる
x += y; // x = x + y と同義。x は 8 になる
printf("a = %d (a =+ b)\n", a);
printf("x = %d (x += y)\n", x);
return 0;
}
a = 3 (a =+ b)
x = 8 (x += y)
代入演算子のコツとベストプラクティス
読みやすさ優先で使う
意図が一目で分かる更新には積極的に複合代入を使うのが良い習慣です。
たとえばsum += x
やcount -= 1
は、コードレビューでの理解が速くなります。
一方、if (total += x)
のように式の値を条件に使う書き方は意図が曖昧になりやすいので避けましょう。
複雑な式は避ける(必要なら括弧)
複合代入はシンプルな更新に向いています。
演算子の優先順位が絡む場合は括弧で明示した方が読みやすいです。
例えばtotal += price * tax
は分かりやすいですが、さらに足し引きが混ざる場合はtotal += (price * tax) - discount;
のように括弧を加えると誤解を防げます。
プロジェクトのコーディング規約に合わせる
スペースの有無(sum += x;
のように演算子の前後に空白を入れるか)や、使用を推奨する場面はチームで決めると統一感が出ます。
また、左辺の評価が1回で済む性質から、arr[index()] += value;
のようなケースではパフォーマンスや副作用の点で利点がありますが、関数呼び出しが隠れて見えにくくなる場合はリファクタリングも検討してください。
複合代入式自体は「値」を返しますが左辺値(lvalue)ではありません。
戻り値に依存する書き方は初学者には読みにくいため、原則として「1文で1つの更新」に留めるのが安全です。
まとめ
複合代入演算子(+=, -=, *=, /=)は、更新の意図を短く明快に表現できる基本テクニックです。
合計やカウンタ、倍率や平均など、日常的な処理で大活躍します。
一方で、整数の切り捨てや型変換、オーバーフロー、0除算といった落とし穴には注意が必要です。
読みにくい複雑な式は避け、括弧や広い型の利用、事前条件チェックなどのベストプラクティスを守りましょう。
これらを踏まえれば、コードはより安全で読みやすくなり、C言語での開発がぐっと快適になります。