如何在C语言中检查有符号整数溢出而不产生未定义行为?

29

有(1):

// assume x,y are non-negative
if(x > max - y) error;

还有第二个条件:

// assume x,y are non-negative
int sum = x + y;
if(sum < x || sum < y) error;

哪种方法更好或是否有更好的方法。


1
实际上,这个重复并不是真正的重复,它在讨论具有明确定义的环绕语义的“无符号”,而在C中,溢出带符号整数是未定义行为。 - Antti Haapala -- Слава Україні
2
没有必要同时检查 sum < xsum < y - Paweł Bylica
这个问题之前被关闭,因为它是 如何检测整数溢出? 的重复,但那个问题是关于 unsigned 的,其中包装是定义良好的行为。对于有符号的 int 来说更难,因为你不能只是先加上去,然后再检查是否溢出,那已经是未定义行为了,因此编译器可以假设没有溢出! - Peter Cordes
可能是如何检测无符号整数乘法溢出?的重复问题。 - GSerg
3个回答

65

在C语言中,整数溢出是“未定义行为”的典型例子(需要注意的是,无符号整数运算永远不会溢出,它们被定义为环绕)。这意味着一旦执行了x+y,如果发生了溢出,则已经无法进行任何检查——你的程序可能已经崩溃。就像检查除以零一样——如果等到除法执行后再检查,那就为时已晚。

因此,这意味着方法(1)是唯一正确的方法。对于max,您可以使用来自<limits.h>INT_MAX

如果x和/或y可能为负数,则事情就变得更加困难——您需要以不会导致溢出的方式进行测试。

if ((y > 0 && x > INT_MAX - y) ||
    (y < 0 && x < INT_MIN - y))
{
    /* Oh no, overflow */
}
else
{
    sum = x + y;
}

顺便问一下,您能否评论一下这个解决方案与其他替代方案相比的性能? - Pacerier
8
与错误的解决方案进行比较毫无意义。你有其他正确的解决方案吗? - caf
2
另一种通常的方法似乎是将其转换为更宽的类型。我不确定第三种选择,但肯定有更多的选择。 - Pacerier
@Pacerier 如果你已经在平台上使用最大的类型,那么将其转换为更宽的类型是不可能的。 - martinkunev
1
@Tuntable:每个编译器都会进行优化,但这可能会导致可怕的后果:http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html - ilstam
显示剩余3条评论

4

您只能使用无符号整数和算术运算来检查溢出:

unsigned a,b,c;
a = b + c;
if (a < b) {
    /* overflow */
}

C语言中,有符号整数的溢出行为是未定义的,但在大多数计算机上,您可以使用

int a,b,c;
a = b + c;
if (c < 0 ? a > b : a < b) {
    /* overflow */
}

这可能需要编译时标志来让编译器强制执行换行语义,并且在使用任何饱和或陷阱算术的机器上无法工作。


9
事后使用有符号整数检查溢出并不正确。这是未定义的行为,因此编译器会愉快地优化掉检查,除非传递一个开关(如-fwrapv)来启用有符号环绕作为一种语言扩展。这不仅是跨架构的可移植性问题。 - strcat
1
有符号整数的溢出也可以进行检查。请参见https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow。 - sbhatla
2
不,现代gcc会破坏你的有符号int示例。ARM64上的有符号整数不会溢出吗? - Peter Cordes

-6

你只需要检查其中一个。如果 x + y 溢出,它将小于 x 和 y。因此:

int sum = x + y;
if (sum < x) error;

应该足够了。

以下网站有关于整数溢出的大量内容:

http://www.fefe.de/intof.html

如果你想处理负数,可以进行扩展:

int sum = x + y;
if (y >= 0) {
   if (sum < x) error;
} else {
   if (sum > x) error;
}

1
原帖指定非负整数,但我已添加了处理负数的代码。 - clahey
21
这不正确-一旦 x+y 溢出,程序就具有未定义行为。在执行溢出操作之前,你必须进行检查-就像你对整数除以零一样。 - caf
如果有什么比这个答案更错误的话,那可能是它链接到的页面。 - Antti Haapala -- Слава Україні
这对于无符号是正确的,我认为对于那种情况非常好。 - Millie Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接