(error): Signed integer overflow for expression ‘y+2‘
警告:’y+2’の計算で符号付き整数オーバーフロー[(error)integerOverflow]
■overflowとwrap aroundの単純な例
// gcc -Wall -Wextra -O2 -fsanitize=undefined
#include <stdio.h>
int main(void){
//ラップアラウンド
unsigned int x = 0xffffFFFF ;
printf("%x\n",x+2);//合法
//オーバーフロー
signed int y = 0x7fffFFFF ;
printf("%x\n",y+2);//未定義動作
}
簡単に言うと
・符号なし計算の桁溢れをラップアラウンドと言い合法。
・符号付き計算の桁溢れをオーバーフローと言い未定義動作。
gcc -Wall -Wextra -O2 -fsanitize=undefined
でコンパイル&実行すると10行目で以下の警告が出ます。
x.c:10:5: runtime error: signed integer overflow: 2147483647 + 2 cannot be represented in type ‘int’
■オーバーフローによる未定義動作の例
#include <stdio.h>
int f(int x,int y) {
if((x+y) < x) {
printf("%x < %x\n",(x+y),x);
return 1;
} else{
printf("%x >= %x\n",(x+y),x);
return 0;
}
}
int main(void) {
printf("%d\n",f(0x7fffFFFF,2));
}
このコードはgcc11 とclangで動作結果が違います。
■符号付きの修正例(オーバーフロー)
#include <stdint.h>
#include <limits.h>
#include <stdio.h>
int main(void){
int32_t x32 = 0x7FFFffff ;
int32_t y32 = 2;
int64_t z64 ;
z64 = x32 + y32 ;//オーバーフロー(未定義動作)
printf("%lx\n",z64);
z64 = (int64_t)x32 + y32;//修正例
printf("%lx\n",z64);
}
clang -Weverything -fsanitize=undefined
でコンパイルすると、
走行時
UndefinedBehaviorSanitizer: undefined-behavior
と教えてくれます。
※コンパイル時は警告は出ません。
■符号無しの修正例(ラップアラウンド)
#include <stdint.h>
#include <limits.h>
#include <stdio.h>
int main(void){
uint32_t x32 = 0xFFFFffff ;
uint32_t y32 = 2;
uint64_t z64 ;
z64 = x32 + y32 ;//ラップアラウンド
printf("%lx\n",z64);
z64 = (uint64_t)x32 + y32;//修正例
printf("%lx\n",z64);
}
8行目でラップアラウンドが発生しますが、
gccもclangも警告を出してくれません。
32bit同士の計算ではラップアラウンドしてしまうので
11行目のようにキャストして64bitの計算を行う必要があります。
補足:
C言語の世界でオーバーフローと言うと
バッファオーバーフロー
(主に配列の範囲外アクセス)と
整数オーバーフロー
(桁あふれ)
がありますが、
この記事では整数オーバーフローを扱いました。
参考: