gpt4 book ai didi

c++ - 在C/C++中检测签名溢出

转载 作者:行者123 更新时间:2023-12-02 10:27:14 25 4
gpt4 key购买 nike

乍一看,这个问题似乎是How to detect integer overflow?的重复,但是实际上却大不相同。

我发现,虽然检测无符号整数溢出非常简单,但是在C / C++中检测带符号溢出实际上比大多数人想象的要困难。

最明显但最幼稚的方法是:

int add(int lhs, int rhs)
{
int sum = lhs + rhs;
if ((lhs >= 0 && sum < rhs) || (lhs < 0 && sum > rhs)) {
/* an overflow has occurred */
abort();
}
return sum;
}

这样做的问题是根据C标准,有符号整数溢出是 undefined 的行为。换句话说,根据标准,甚至在导致签名溢出时,程序就如同取消引用空指针一样无效。因此,您不能导致 undefined 的行为,然后尝试在事后检测溢出,如上述后置条件检查示例中所示。

即使上面的检查可能在许多编译器上都有效,但您不能指望它。实际上,由于C标准说 undefined 有符号整数溢出,因此某些编译器(如GCC)在设置优化标志时会使用 optimize away the above check,因为编译器认为有符号溢出是不可能的。这完全中断了检查溢出的尝试。

因此,另一种检查溢出的可能方法是:
int add(int lhs, int rhs)
{
if (lhs >= 0 && rhs >= 0) {
if (INT_MAX - lhs <= rhs) {
/* overflow has occurred */
abort();
}
}
else if (lhs < 0 && rhs < 0) {
if (lhs <= INT_MIN - rhs) {
/* overflow has occurred */
abort();
}
}

return lhs + rhs;
}

这似乎更有希望,因为在事先确保执行此类加法操作不会导致溢出之前,我们实际上不会将两个整数相加。因此,我们不会引起任何 undefined 的行为。

但是,不幸的是,该解决方案的效率比初始解决方案低很多,因为您必须执行减法运算只是为了测试加法运算是否有效。即使您不关心这种(小的)性能下降,我仍然不完全相信此解决方案是足够的。 lhs <= INT_MIN - rhs表达式似乎完全类似于编译器可能会优化掉的那种表达式,认为有符号溢出是不可能的。

那么,这里有更好的解决方案吗?是否可以保证1)不会导致 undefined 的行为,以及2)不能为编译器提供优化溢出检查的机会?我当时想通过将两个操作数都转换为无符号,然后通过滚动自己的二进制补码算术执行检查可能有某种方法,但是我不确定如何做到这一点。

最佳答案

您的减法方法是正确且定义明确的。编译器无法对其进行优化。

如果您有较大的整数类型可用,另一种正确的方法是在较大的类型上执行算术,然后在将其转换回时检查结果是否适合较小的类型

int sum(int a, int b)
{
long long c;
assert(LLONG_MAX>INT_MAX);
c = (long long)a + b;
if (c < INT_MIN || c > INT_MAX) abort();
return c;
}

一个好的编译器应将整个加法和 if语句转换为 int大小的加法和单个条件溢出溢出,并且切勿实际执行较大的加法。

编辑:正如Stephen指出的那样,我在获取(不太好的)编译器gcc生成合理的asm时遇到了麻烦。它生成的代码并不是很慢,但是肯定不是最优的。如果有人知道此代码的变体,这些变体将使gcc做正确的事情,我很乐意看到它们。

关于c++ - 在C/C++中检测签名溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63855954/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com