Code Fix

中級

int型の最大値に1を足すと最小値(負の数)に折り返る

INT_MAXに1を足すと、多くの環境ではINT_MINまで折り返り、突然大きな正の数が大きな負の数に変わります。

なぜエラーが出ないのか

出力: -2147483648
(エラーなし)
Cは何も報告しません。コンパイルも実行も正常に完了するためです
出力: -2147483648
実際の挙動 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  #include <limits.h>
 2  #include <stdio.h>
 3  
 4  int main(void) {
 5      int x = INT_MAX;
 6      long y = (int         )x + 1;
                        ^
 7      printf("%ld\n", y);
 8      return 0;
 9  }
出力: -2147483648期待: 2147483648

(int)x + 1はint型のまま加算されるため、INT_MAXを超えた瞬間に折り返り負の数になります。longにキャストしてから足せば、加算そのものがlong型で行われるため折り返りません。

直し方: int を long にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  #include <limits.h>
 2  #include <stdio.h>
 3  
 4  int main(void) {
 5      int count = INT_MAX - 1;
 6      long total = (int         )count + 3;
                            ^
 7      printf("%ld\n", total);
 8      return 0;
 9  }
出力: -2147483647期待: 2147483649

countがINT_MAXに近い値の場合、3を足しただけでも簡単にオーバーフローします。加算前にlongへ広げておくことが対策になります。

直し方: int を long にします。

この問題を解いてみる →

パターン3

 1  #include <limits.h>
 2  #include <stdio.h>
 3  
 4  int main(void) {
 5      int stock = INT_MAX;
 6      long updated = (int         )stock + 100;
                              ^
 7      printf("%ld\n", updated);
 8      return 0;
 9  }
出力: -2147483549期待: 2147483747

在庫数や合計値を数える変数の型が小さすぎると、長時間動作するプログラムで突然この種のオーバーフローが発生します。

直し方: int を long にします。

この問題を解いてみる →

パターン4

 1  #include <limits.h>
 2  #include <stdio.h>
 3  
 4  int main(void) {
 5      int n = INT_MAX;
 6      long r = (int         )n + 5;
                        ^
 7      printf("%ld\n", r);
 8      return 0;
 9  }
出力: -2147483644期待: 2147483652

intのままだと加算もint型で行われ、INT_MAXを超えた瞬間に折り返ります。longにキャストしてから足す必要があります。

直し方: int を long にします。

この問題を解いてみる →

パターン5

 1  #include <limits.h>
 2  #include <stdio.h>
 3  
 4  int main(void) {
 5      int base = INT_MAX - 2;
 6      long sum = (int         )base + 10;
                          ^
 7      printf("%ld\n", sum);
 8      return 0;
 9  }
出力: -2147483641期待: 2147483655

baseがINT_MAXに近い値の場合、少し足しただけでも簡単にオーバーフローします。

直し方: int を long にします。

この問題を解いてみる →

よくある誤解

エラーにも警告にもなりません。C言語の符号付き整数のオーバーフローは未定義動作と定義されていますが、多くのコンパイラ・環境では実質的に2の補数表現に基づいてビットがそのまま折り返ります。合計値や個数を数える変数の型が小さすぎると、長時間動作するプログラムで突然発生することがあります。

実務での勘所

符号付き整数のオーバーフローが未定義動作であることは、単に「折り返る」以上の実害を生むことがあります。最適化を行うコンパイラは「オーバーフローは起きない」という前提で命令を組み立ててよいため、if (x + 1 > x)のようなオーバーフロー検出のつもりで書いたチェックを、常にtrueとみなして丸ごと消してしまうことがあります。オーバーフローを安全に検出したい場合は、unsigned型で計算するか、__builtin_add_overflowのような専用の組み込み関数を使います。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)