updated:

对一段补码算术代码的分析


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
#include <stdio.h>
#include <limits.h>
int tsub\_ok(int x, int y); //判断x+y是否溢出,不溢出则返回1
int tadd\_ok(int x, int y); //判断x-y是否溢出,不溢出则返回1
int tadd\_ok(int x, int y)
{
int sum = x + y;
if (sum > 0 && x < 0 && y < 0) {
return 0;
} else if (sum < 0 && x > 0 && y > 0) {
return 0;
} else {
return 1;
}
}
int tsub\_ok(int x, int y)
{
return tadd\_ok(x, -y);
}
int main()
{
int x = -56;
int y = INT\_MIN;
printf("%d\\n", tsub\_ok(x, y));
printf("%d\\n", b);
return 0;
}

上面tsub_ok函数的写法看似没有问题,但其实存在一个关于INT_MIN的隐蔽问题。当且仅当x-y中的y取INT_MIN时,无论x取何值,函数的输出都与事实相反。原因正在于补码的不对称性:-INT_MIN并不在补码的范围内,此时就发生了溢出。

溢出的方式也很巧,-INT_MIN的十六进制应该是0x80000000(无符号编码),然而int类型是有符号的,该编码被以补码形式又解释为了INT_MIN。因此,在32位补码的世界里,-INT_MIN = INT_MIN。

因此就出现了这样的情况:y = INT_MIN,x为负值时,程序判断会发生溢出;x为非负值时,程序判断不会溢出,都与事实相悖(注意当x取0时,由于补码的不对称,运算事实上还是会溢出的)。


← Prev base64隐写的一种解决办法 | Bash shell编程tips Next →