我们知道CPython在数字变大时会无声地将整数提升为长整数(允许任意精度算术)。
我们如何检测纯C中int
和long long
的溢出?
答案 0 :(得分:29)
您不能检测签名的int
溢出。您必须编写代码以避免。
签名的int溢出是未定义的行为,如果程序中存在它,则该程序无效,并且不需要编译器生成任何特定的行为。
答案 1 :(得分:18)
您可以预测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;
}
请参阅此paper,以获取更多信息。您还可以在同一篇文章中找到为什么无符号整数溢出不是未定义的行为以及可能是可移植性的问题。
编辑:
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)
访问此link。
编辑:
关于某人提出的问题
我认为,解释为什么有符号的int溢出未定义,而无符号的却不是这样。
答案取决于编译器的实现。大多数C实现(编译器)使用的最简单的溢出行为都是使用其所使用的整数表示形式。
实际上,带符号值的表示形式可能有所不同(根据实现方式):one's complement
,two's complement
,sign-magnitude
。对于无符号类型,标准没有理由允许变化,因为只有一个明显的binary representation
(标准仅允许二进制表示)。
答案 2 :(得分:9)
带符号的操作数必须在添加之前进行测试。这是在所有情况下均进行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;
}
}