如何确定整数的溢出
当数值运算结果超出数据类型所能表示的范围时,会发生溢出(overflow)。C语言中整数溢出是一种未定义行为(undefined behavior)。本文以整数的加法t = a + b为例,探讨一下如何确定整数的溢出。
一、无符号整数
根据ISO/IEC 9899:2018标准第6.2.5 Types节,无符号整数的运算结果永远不会溢出。如果运算结果超出对应无符号整数类型表示的范围,运算结果将会减模(模 = 对应无符号整数类型最大值 + 1),直至结果能够使用对应无符号整数类型表示,例如:
printf("%u\n", (unsigned char)257);
将输出:
1
unsigned char类型的最大值为255,所以模为256;257超出了unsigned char类型的表示范围,因此需要减模,257-256结果为1。
减模得到的运算结果往往不是期望的运算结果,所以无符号整数的运算也需要确认运算结果是否超出对应数据类型所能表示的范围。
对于表达式t = a + b,如果a + b超出对应数据类型表示的范围,t = a + b - 模,因此t < a且t < b。
假设t、a、b的数据类型都是unsigned char类型,以下是一个判断和是否超出unsigned char类型表示范围的范例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
/*检查unsigned char类型整数的和是否超出表示范围。*/
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
/*声明unsigned char版本的检查函数。*/
bool unsignedCharOverflowCheck(unsigned char, unsigned char);
int main(void)
{
printf("Unsigned char type integer value range: %u ~ %u\n", 0, UCHAR_MAX);
unsignedCharOverflowCheck(124,156);
unsignedCharOverflowCheck(102,108);
return 0;
}
/*定义unsigned char版本的检查函数。*/
bool unsignedCharOverflowCheck(unsigned char a, unsigned char b)
{
if((unsigned char)(a + b)<a)
{
printf("%u + %u exceeds the representation range of unsigned char type.\n", a, b);
return true;
}
else
{
printf("%u + %u is within the representation range of unsigned char type.\n", a, b);
return false;
}
}
|
(a + b)运算时会发生整型提升(integer promotions),所以应使用(unsigned char)强制类型转换限定结果类型;如果不使用强制类型转换,(a + b)<a值将永远为0。
二、有符号整数
当两个有符号整数相加时,如果两个操作数都是正数,结果为负数时,则发生了正溢出;如果两个操作数都是负数,结果为正数时,则发生了负溢出;如果任意操作数为0,或者一个操作数为正数,另一个操作数为负数时,则不会发生溢出。
假设t、a、b的数据类型都是signed char类型,以下是一个判断和是否超出signed char类型表示范围的范例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
/*检查signed char类型整数的和是否溢出。*/
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
/*声明signed char版本的检查函数。*/
bool signedCharOverflowCheck(signed char, signed char);
int main(void)
{
printf("Signed char type integer value range: %d ~ %d\n", CHAR_MIN, CHAR_MAX);
signedCharOverflowCheck(-80, -100);
signedCharOverflowCheck(-80, 100);
signedCharOverflowCheck(80, 100);
return 0;
}
/*定义signed char版本的检查函数。*/
bool signedCharOverflowCheck(signed char a, signed char b)
{
if(((a<0)==(b<0)) && (((signed char)(a+b)<0) != (a<0)))
{
printf("(%d) + (%d) will overflow.\n", a, b);
return true;
}
else
{
printf("(%d) + (%d) will not overflow.\n", a, b);
return false;
}
}
|
表达式(a<0)==(b<0)用于判断两个操作数的符号是否相同,符号相同才有溢出的可能;表达式((signed char)(a+b)<0) != (a<0)用于判断和的符号和操作数的符号是否相同,符号相同,则没有溢出;符号不同,则发生了溢出。
主要参考资料:
1、ISO/IEC 9899:2018
2、《深入理解计算机系统》原书第3版 作者:Randal E. Bryant David R. O'Hallaron 出版社:机械工业出版社