📦 変数・型・演算子
🎯 オーバーフローが起きる仕組みと危険性を理解し、桁の見積もりと予防策を実行できる
動画サイトの再生回数が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)や(double)のように型名を()で書いて、値を別の型へ明示的に変換すること。意図をはっきり示して事故を防ぐための書き方。int同士の割り算を小数で得たいときなど、意図的に型を変えたい場面で使う。
変数に入れられる値の種類と、確保するメモリの大きさを決める分類のこと。箱の形や大きさを決めるルールのようなものだと考えるとよい。int・double・charなど、C言語にはあらかじめ複数の型が用意されている。
もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。