如何在C中检测整数溢出

Dea*_*ean 20 c overflow

我们知道CPython在数字变大时会无声地将整数提升为长整数(允许任意精度算术)。

我们如何检测的溢出intlong long纯C?

Jes*_*uhl 30

您无法检测到签名int溢出。您必须编写代码来避免它。

有符号的int溢出是未定义的行为,如果程序中存在该溢出,则该程序无效,并且不需要编译器生成任何特定的行为。

  • 我认为,解释为什么有符号的int溢出未定义,而无符号的却不是这样,这将是非常有益和有益的。 (7认同)
  • @hetepeperfan因为这是语言标准所说的。 (7认同)
  • @sneftel多数民众赞成在一个权威的论据缺乏权威的来源,鄙视它可能是正确的。最重要的是,一旦人们开始理解语言,标准就变得更加有意义,这也许就是他们首先访问stackoverflow的原因。 (6认同)
  • @hetepeperfan解释为什么按原样编写标准的原因,在很大程度上超出了Stack Overflow的范围。 (5认同)
  • 您可以在进行计算之前检查输入值,以防止溢出。 (3认同)
  • @hetepeperfan关于该选择背后的原因,肯定已经对Stackoverflow进行了问答。因此,没有理由在OP从未询问过的地方进行讨论。 (2认同)

abh*_*ora 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;
}
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参考本文。您还可以在同一篇文章中找到为什么无符号整数溢出不是未定义的行为以及可能是可移植性的问题。

编辑:

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)
Run Code Online (Sandbox Code Playgroud)

访问此链接

编辑:

关于某人提出的问题

我认为,解释为什么有符号的int溢出未定义,而无符号的却不是这样,这将是非常有益和有益的。

答案取决于编译器的实现。大多数C实现(编译器)使用的最简单的溢出行为就是使用它使用的整数表示形式。

在实践中,签署值表示可能会有所不同(根据实现): ,one's complement,。two's complement sign-magnitude对于无符号类型,标准没有理由允许变化,因为只有一个明显的binary representation(标准仅允许二进制表示)。

  • 同样,两者在技术上都称为溢出。下溢表示该值的*大小*太小,无法在浮点变量中表示。 (9认同)
  • @AnttiHaapala它不会“忽略”没有可能溢出的情况“ a == 0”,它只是以不同的方式处理它。 (3认同)

chq*_*lie 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果long long已知类型的范围大于type 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;
    }
}
Run Code Online (Sandbox Code Playgroud)