C言語学習ポータル(無料版) 学習コンテンツ 変数・型・演算子 オーバーフローという事件

📦 変数・型・演算子

オーバーフローという事件

無料公開版に収録・読む目安 13分

🎯 オーバーフローが起きる仕組みと危険性を理解し、桁の見積もりと予防策を実行できる

動画サイトの再生回数が21億を超えたとき、カウンタの表示がおかしくなった——という事件が実際にありました。原因は int の限界です。箱に入りきらない値を入れると何が起きるのか。オーバーフローという事件を見ます。

限界の1歩先

#include <stdio.h>
#include <limits.h>

int main(void) {
    int n = INT_MAX;

    printf("%d\n", n);
    printf("%d\n", n + 1);   // 危険:符号付きのオーバーフロー
    return 0;
}

多くの環境では 2147483647 の次に -2147483648 が出ます。最大値の1つ上が最小値。メーターが一周して戻ってしまう動きです。

ただしCの規格では、符号付き整数のオーバーフローは未定義動作とされています。「必ず一周する」ことすら保証されていません。最適化の結果、はみ出しを調べる条件文が丸ごと消される、といった直感に反する挙動も現実に起きます。

符号なしは折り返しが決まっている

#include <stdio.h>

int main(void) {
    unsigned char n = 250;

    printf("%u\n", n);
    n = n + 5;   // 255(この型の最大)
    printf("%u\n", n);
    n = n + 1;   // 一周して0
    printf("%u\n", n);
    return 0;
}

出力は 250 / 255 / 0。符号なしの型は「最大値の次は0」と規格で決められています(時計の針と同じ考え方)。決まっているぶん扱いやすいものの、意図しない一周は同じように事故になります。

気づきにくいのは掛け算

#include <stdio.h>

int main(void) {
    int hitori = 50000;
    int ninzuu = 50000;
    int goukei = hitori * ninzuu;             // 25億 → int に入らない
    long long anzen = (long long)hitori * ninzuu;

    printf("%d\n", goukei);
    printf("%lld\n", anzen);
    return 0;
}

5万×5万=25億は int の範囲外です。goukei はでたらめな値になり、anzen は正しく 2500000000 と出ます。ポイントは (long long) を掛ける前に付けていること。(long long)(hitori * ninzuu) では、壊れた後の値を入れ直すだけです。

現実に起きた事件

・2038年問題 … 時刻を32ビットの秒数で持つシステムが、2038年1月19日に一周します。
・Ariane 5(1996年) … 大きな小数を16ビット整数へ変換して溢れ、打ち上げ37秒後に爆発。損害は数百億円規模でした。
・ゲームの所持金が突然マイナスになる、といったバグの多くもこの仲間です。

師範器の大きさを問わずに水を注げば、こぼれる。プログラムのこぼれは床を濡らさぬゆえ、誰も気づかぬまま船が沈むのじゃ。

防ぎ方

・桁を先に見積もる。 合計・積・秒数が21億を超えそうなら long long にします。
・足す前に確かめる。 a > INT_MAX - b なら、足すと溢れると分かります。
・C23の安全な演算。 <stdckdint.h> の ckd_add などは、溢れたかどうかを戻り値で教えてくれます(トラック23で扱います)。

まとめ

int の限界を超えると値は静かに壊れます。符号付きは未定義動作、符号なしは0へ折り返し。合計や掛け算では桁を先に見積もり、必要なら long long を計算の前に使います。エラーが出ないぶん、自分で気づく仕組みを持つことが唯一の防御です。

要点まとめ

アプリで演習問題まで解く(無料・登録不要)

この項目に出てくる用語

オーバーフローおーばーふろー

型が表現できる範囲を超えた値を扱おうとして、値がおかしくなってしまう現象のこと。整数型では想定外に小さい値へ化けることがある。int型の最大値に1を足すと、最小値付近の値へ回り込んでしまうことがある。

int型いんとがた

整数を扱うための基本的な型のこと。小数点を持たない数値を格納する、C言語でもっとも使う機会の多い型のひとつにあたる。環境にもよるが、多くの場合4バイトの大きさで値を保持している。

キャストきゃすと

(int)や(double)のように型名を()で書いて、値を別の型へ明示的に変換すること。意図をはっきり示して事故を防ぐための書き方。int同士の割り算を小数で得たいときなど、意図的に型を変えたい場面で使う。

型かた

変数に入れられる値の種類と、確保するメモリの大きさを決める分類のこと。箱の形や大きさを決めるルールのようなものだと考えるとよい。int・double・charなど、C言語にはあらかじめ複数の型が用意されている。

同じトラックのほかの項目

変数とは名前付きの箱宣言・代入・初期化を区別し、型のついた変数を正しく作って使える int型と整数の世界intの範囲と限界値の調べ方を知り、long long・unsignedの使いどころが分かる 浮動小数点数 float / doubledoubleを既定として使い分け、小数に必ず誤差があることと比較の作法を理解する char型と文字コードcharが1バイトの小さな整数であることを理解し、文字と数値を相互に変換できる bool型と真偽——C23標準の書き方C23のbool/true/falseをそのまま使い、Cにおける真偽(0が偽・0以外が真)を説明できる 定数——const と constexprconstとconstexpr(C23)を使い分け、変わらない値を宣言時に固定できる 算術演算子と優先順位5つの算術演算子と優先順位を理解し、複合代入・インクリメントを正しく使える 整数の割り算と剰余の罠整数どうしの割り算が切り捨てになる理由を理解し、平均計算などで正しく対処できる 型変換——暗黙と明示暗黙の型変換が起きる方向を知り、キャストで意図を明示できる 2進数・16進数リテラル(0b / 0x / 桁区切り')0b・0x・8進の書き方の違いを理解し、C23の桁区切りで読みやすく数値を書ける sizeofとメモリ上のサイズsizeofで型と変数のバイト数を測り、型ごとに箱の大きさが違うことを実感できる
‹ sizeofとメモリ上のサイズ

もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。

完全版(BOOTH)を見る

📚 姉妹教材:手を動かして覚える Linux 教材 — Linuxとインフラの仕組みを地図で学ぶ

Web版 / BOOTH