C言語では、整数型に対する算術演算がオーバーフロー(overflow)を引き起こすことがあります。これは、結果が型の表現可能な範囲(例えば、intまたはunsigned int)を超えるときに起こります。オーバーフローに関する動作の特性は、言語の標準によって定められています。
符号付き型のオーバーフロー(signed overflow)は未定義の動作(undefined behavior)を引き起こし、つまりコンパイラは任意の行動を取る権利を持ちます:エラーを無視したり、例外を生成したり、予測不可能な結果を残したりすることがあります。無符号型(unsigned)に関しては、Cの標準に従い、オーバーフローの動作は定義されています:型のサイズでモジュールに戻り込みます(wraparound)。
無符号数では、オーバーフロー結果は簡単に予測できます。例えば、UINT_MAX + 1 == 0。符号付き数については、操作の前に、<limits.h>内のマクロを使って型の境界をチェックすることが推奨されます。また、現代のコンパイラやツールは潜在的なオーバーフローを検出することができます。
コード例:
#include <stdio.h> #include <limits.h> int add_with_check(int a, int b) { if (a > 0 && b > INT_MAX - a) { printf("オーバーフローが発生します!\n"); return -1; } return a + b; } int main() { int x = INT_MAX, y = 1; printf("結果: %d\n", add_with_check(x, y)); unsigned int ux = UINT_MAX; printf("無符号オーバーフロー: %u\n", ux + 1); return 0; }
主な特徴:
<limits.h>を使って型のサイズを取得します無符号型のオーバーフローはエラーですか?
いいえ、その動作は標準で定義されており、モジュールで戻り込むことと等価です。例えば、(unsigned int)UINT_MAX + 1 == 0は常に真です。
オーバーフロー時にintの結果が単純に"INT_MIN"を越えると信頼できますか?
いいえ、そのような動作は保証されておらず、標準化されていません。これは未定義の動作です。クラッシュしたり、正しくない(プラットフォームにより異なる)値を返したり、コンパイラによって予測不可能に最適化されたりすることがあります。
intが常に二の補数であるという振る舞いを期待できますか?
現代のハードウェアはほとんど常に符号付きintを二の補数で表現しますが、C言語は標準でこれを要求していないため、オーバーフローのあるコードは移植性がありません。
境界のチェックなしでintの数を加えることは、大きなデータの場合にオーバーフローを引き起こし、不正な計算をもたらします。
利点:
欠点:
すべての算術操作の前に、マクロや関数を使ってオーバーフローをチェックします。wraparoundが許可されている場所で無符号を使用します。
利点:
欠点: