检测 C 中的整数溢出


#include <stdio.h>
int reverse(int);
int reverse(int x) {
int negative = 0;
if (x < 0)
negative = x;
if (negative != 0) {
x = 0 - x;
}
int y = 0, temp;
while (x > 0) {
temp = x % 10;
x = x / 10;
y = (y * 10) + temp;
}
if (negative == 0) {
return y;
} else {
return 0 - y;
}
}
int main() {
int x, y;
printf("Enter your number: n");
scanf("%d", &x);
y = reverse(x);
printf("The reversed number is: %dn", y);
return 0;
}

此程序反转有符号整数。我无法检查受人尊敬的整数 y 是否超出界限。我无法澄清溢出的话题。如果我要求使用 scanf 的编译器扫描一个整数并输入一个超出整数范围的整数,会发生什么?值在存储时会更改吗?

您似乎有两个关于溢出的不同问题。

  1. 如何检测整数算术中的溢出?
  2. 如何在扫描等转换函数中检测卵流?

答案如下。

  1. 没有通用的方法,您必须根据具体情况进行操作。例如,如果y <= INT_MAX / 10,则可以确定y * 10不会溢出。... + temp也是一样.
  2. scanf和朋友除了限制字段宽度和输入范围之外,没有任何方法可以保护您免受溢出的影响(但是如果您正在读取小数,则无法将范围完全限制为INT_MAX(。如果扫描的值不适合目标类型,则行为未定义。安全转换具有确切范围的字符串的唯一方法是与strtol和朋友一起。这些函数检测溢出并相应地设置errno

在标准 C 中,溢出是一种特殊情况,因此根据标准未定义行为。从草案 n1570 为 C11, 6.5 表达式 § 5:

如果在计算表达式期间出现异常情况(即,如果 结果未在数学上定义或不在其可表示值的范围内 类型(,行为未定义。

这意味着您不能按照每个标准定义的方式处理溢出。话虽如此,大多数编译器只是保留结果中适合类型表示的低阶位,而忽略最高阶位。这就是MSVC,gcc和clang所做的。

从现在开始,我将假设您的系统使用int32_t(或。int16_tint64_t( 表示已签名的 int 并忽略溢出。它不是每个标准强制要求的,但足够常见,可能是您的系统所做的。

代码中有 2 个可能的溢出。首先是scanf函数:正如 @n.m. 所说,如果输入序列不适合该类型,则不会对这个函数族的行为进行任何说明。我将假设提交的数字可以作为签名的 int(在 INT_MIN 到 INT_MAX 之间(。无论如何,让用户检测到它是微不足道的:只需显示x:如果它不是刚刚输入的数字,则发生溢出。

第二个是这里:

y = (y * 10) + temp;

同样,通过执行反向操作并控制一切正常,可以轻松测试溢出:

int next = (y * 10) + temp;
if ((next < 0) || (y != (next - temp) / 10)) {
// an overflow occured...
...

理论上,没有什么可以保证否定正 int 是有效的负 int,但对于intXX_t类型来说也是如此,因此这应该足以检测reverse函数中的溢出。

最新更新