
C言語基礎|オーバフローと例外
あふれた瞬間、数は別人になる。オーバフローを“安全に扱う技術”を身につけよう。
オーバフローは「バグの入り口」になりやすい
整数型って、表せる範囲が決まっている“有限の箱”でしたよね。
だから計算結果が箱からはみ出した瞬間に、プログラムの挙動が一気に怪しくなります。
この「はみ出し」が オーバフロー(overflow)。
そして「0で割る」みたいに計算として成り立たない状況も含めて、広い意味で 例外的な状況 として扱われます。
ただしここ、C言語の大事なポイントがあります。
- 符号付き整数のオーバフローは、C言語では未定義動作(undefined behavior)
→ 例外が出るとは限らないし、たまたま動いて見えても、最適化で壊れることがあります。 - 符号無し整数は、必ず“剰余(あまり)”になる(いわゆるラップアラウンド)
→ 「最大値 + 1」で割った余り、というルールが決まっています。
この差を表と図でしっかり整理していきますね。

まず整理:符号付きと符号無しで何が違うの?
オーバフロー時の基本ルール(C言語)
| 種類 | 例 | 範囲を超えたとき | 期待できること |
|---|---|---|---|
| 符号付き整数(int など) | INT_MAX + 1 | 未定義動作 | 結果・動作は保証されない(落ちる場合も、落ちない場合もある) |
| 符号無し整数(unsigned など) | UINT_MAX + 1U | 剰余になる | 常に (数学的結果) % (最大値 + 1) |
表の説明
- 符号付きは「こうなる」と決められていません。だから危険。
- 符号無しは「必ずこうなる」と決められています。だから予測できる。
図でイメージ:箱の端っこで何が起きる?
図1:符号付き整数(範囲の端で“保証がない”)

図2:符号無し整数(円環:ぐるっと回る)

図の説明
- 符号無しは「0〜最大値を順番に使い回す」イメージです。
- だから UINT_MAX + 1U は 0 に戻ります。
よく出てくる例外的な状況:0による除算
オーバフロー以外で代表的なのが 0による除算 です。
- x / 0
- x % 0
これは数学的に定義できないので、C言語では未定義動作になります。
多くの環境では実行時に異常終了しますが、これも「必ずそうなる」とは言い切れない点がC言語っぽいところです。
“例外”という言葉の扱い(C言語らしい注意)
質問文では「例外が発生する」と書かれていましたが、C言語の規格としては次の理解が安全です。
- 符号付きのオーバフローや 0 除算は 未定義動作
→ 例外が出るかどうか、強制終了するかどうかは環境次第 - ただし実行環境によっては、CPU例外やランタイム検出で落ちることがある
→ 「落ちることが多い」は事実としても、「必ず落ちる」は言えません
なので、教材としては 落ちるかどうかより、未定義動作を起こさない設計 を優先するのが一番スッキリします。
安全にするための実務的なコツ
よく使う対策
| 状況 | 対策の例 | ねらい |
|---|---|---|
| int の加算が危ない | INT_MAX / INT_MIN を使って事前判定 | 未定義動作を回避 |
| 計算結果が大きくなりそう | より広い型で計算(long long など) | 途中計算のはみ出し防止 |
| 符号無しの剰余が欲しい | unsigned を使う | ルールが明確で予測できる |
| 0除算があり得る | 割る前に if (y == 0) で分岐 | 未定義動作を回避 |
演習問題
演習7-6:符号無しの剰余ルールを確認しよう
unsigned の最大値が分かるように UINT_MAX を使い、次を確認するプログラムを作成せよ。
- UINT_MAX + 1U が 0 になる
- UINT_MAX + 2U が 1 になる
- UINT_MAX - 1U に 3U を足すと 1 になる(ぐるっと回る)
解答例
プロジェクト名:chap7-23-1 ソースファイル名:chap7-23-1.c
#include <stdio.h>
#include <limits.h>
int main(void)
{
unsigned a = UINT_MAX;
printf("符号無し整数は剰余で回ります。\n");
printf("UINT_MAX = %u\n", a);
printf("UINT_MAX + 1U = %u\n", a + 1U);
printf("UINT_MAX + 2U = %u\n", a + 2U);
unsigned b = UINT_MAX - 1U;
printf("UINT_MAX - 1U = %u\n", b);
printf("(UINT_MAX - 1U) + 3U = %u\n", b + 3U);
return 0;
}実行結果例
符号無し整数は剰余で回ります。
UINT_MAX = 4294967295
UINT_MAX + 1U = 0
UINT_MAX + 2U = 1
UINT_MAX - 1U = 4294967294
(UINT_MAX - 1U) + 3U = 1解説
- unsigned は 0〜最大値までが循環するので、最大値の次は 0 になります。
- つまり計算結果は常に (数学的結果) % (最大値 + 1) です。
- この性質はビット演算や暗号、ハッシュ、リングバッファなどで便利に使われますが、意図せず起きるとバグの原因にもなるので「知って使う」が大事です。
