我们知道CPython在数字变得更大时会自动将整数提升为长整型(允许任意精度算术)。
在纯C中,我们如何检测int
和long long
的溢出?
我们知道CPython在数字变得更大时会自动将整数提升为长整型(允许任意精度算术)。
在纯C中,我们如何检测int
和long long
的溢出?
你无法检测有符号的 int
溢出。你必须编写代码来避免它。
有符号的 int 溢出是 未定义行为,如果在程序中存在,程序就是无效的,编译器不需要生成任何特定的行为。
在进行有符号加法运算之前,您必须检查可能的溢出。尝试在求和后检测它是没有用的。您可以预测 signed int overflow
,但在求和后尝试检测它已经太晚了。
通过在求和后测试来避免未定义行为是不可能的。如果加法溢出,那么已经存在未定义行为。
如果是我的话,我会这样做:
#include <limits.h>
int safe_add(int a, int b)
{
if (a >= 0) {
if (b > (INT_MAX - a)) {
/* handle overflow */
}
} else {
if (b < (INT_MIN - a)) {
/* handle underflow */
}
}
return a + b;
}
请参考这篇论文以获取更多信息。在同一篇论文中,您还可以找到为什么无符号整数溢出不是未定义的行为以及可能存在的可移植性问题。
编辑:
GCC和其他编译器有一些规定来检测溢出。例如,GCC
具有以下内置函数,允许执行简单的算术运算,并检查是否溢出。
bool __builtin_add_overflow (type1 a, type2 b, type3 *res)
bool __builtin_sadd_overflow (int a, int b, int *res)
bool __builtin_saddl_overflow (long int a, long int b, long int *res)
bool __builtin_saddll_overflow (long long int a, long long int b, long long int *res)
bool __builtin_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res)
bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)
bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long long int *res)
访问此链接。
编辑:
关于某人提出的问题:
我认为,解释为什么带符号整数溢出未定义而无符号整数明显不是一个好主意,这将很好地解释并提供信息。
答案取决于编译器的实现。大多数C实现(编译器)只是使用最容易实现的整数表示的溢出行为。
实际上,表示有符号值的方法可能不同(根据实现): 补码
, 反码加1
, 原码
。对于无符号类型,标准没有理由允许变化,因为只有一种明显的二进制表示
(标准只允许二进制表示)。
a == 0
,这是隐含在那里的,即没有保存任何东西。 - Antti Haapala -- Слава Україніa == 0
,即没有可能发生溢出的情况,只是以不同的方式处理。 - chqrliea
小于0时才会执行INT_MIN - a
,这意味着结果将是INT_MIN
加上最多为INT_MAX
的数字。因此,结果将介于INT_MIN
和0
之间,因此不会发生溢出。 - Eyal在执行加法之前,必须测试带符号的操作数。这里是一个安全的加法函数,在所有情况下都进行了2次比较:
#include <limits.h>
int safe_add(int a, int b) {
if (a >= 0) {
if (b > INT_MAX - a) {
/* handle overflow */
} else {
return a + b;
}
} else {
if (b < INT_MIN - a) {
/* handle negative overflow */
} else {
return a + b;
}
}
}
如果已知类型long long
的范围比类型int
大,您可以使用这种方法,可能会更快:#include <limits.h>
int safe_add(int a, int b) {
long long res = (long long)a + b;
if (res > INT_MAX || res < INT_MIN) {
/* handle overflow */
} else {
return (int)res;
}
}