C:标准和编译器中的整数溢出

由Carl Norum编辑,包括适当的标准参考。

C标准规定

如果在计算表达式期间发生exception情况 (即,如果结果未在数学上定义或未在其类型的可表示值范围内),则行为未定义。

是否有编译器开关可以保证整数溢出时的某些行为? 我想避免鼻腔恶魔。 特别是,我想强制编译器包装溢出。

为了唯一性,让标准为C99,编译器为gcc。 但我会对其他编译器(icc,cl)和其他标准(C1x,C89)的答案感兴趣。 事实上,为了惹恼C / C ++人群,我甚至欣赏C ++ 0x,C ++ 03和C ++ 98的答案。

注:国际标准ISO / IEC 10967-1可能与此相关,但据我所知,仅在资料性附录中提及。

看看-ftrapv-fwrapv

-ftrapv

此选项为加法,减法,乘法运算的带符号溢出生成陷阱。

-fwrapv

此选项指示编译器假设加法,减法和乘法的带符号算术溢出使用二进制补码表示。 此标志启用一些优化并禁用其他优化。 默认情况下,Java前端根据Java语言规范的要求启用此选项。

对于你的C99答案,我认为6.5表达式 ,第5段是你正在寻找的:

如果在计算表达式期间发生exception情况 (即,如果结果未在数学上定义或未在其类型的可表示值范围内),则行为未定义。

这意味着如果你遇到溢出,你就不走运了 – 没有任何保证的行为。 无符号类型是一种特殊情况,永不溢出( 6.2.5类型 ,第9段):

涉及无符号操作数的计算永远不会溢出,因为无法通过生成的无符号整数类型表示的结果将以比结果类型可以表示的最大值大1的数量为模。

C ++有相同的陈述,措辞有点不同:

  • 5表达 ,第4段:

    如果在评估表达式期间,结果未在数学上定义或未在其类型的可表示值范围内,则行为未定义。 [ 注意:大多数现有的C ++实现忽略整数溢出。 除零处理,使用零除数形成余数,所有浮点exception因机器而异,通常可通过库函数调整。 -endnote ]

  • 3.9.1基本类型 ,第4段:

    声明unsigned符号的无符号整数应遵守算术模2 ^ n的定律,其中n是该特定整数大小的值表示中的位数。

在C99中,一般行为以6.5 / 5表示

如果在计算表达式期间发生exception情况 (即,如果结果未在数学上定义或未在其类型的可表示值范围内),则行为未定义。

无符号类型的行为在6.2.5 / 9中描述,它基本上表明对无符号类型的操作永远不会导致exception条件

涉及无符号操作数的计算永远不会溢出,因为无法通过生成的无符号整数类型表示的结果将以比结果类型可以表示的最大值大1的数量为模。

GCC编译器有一个特殊选项-ftrapv ,用于捕获有符号整数操作的运行时溢出。

为了完整起见,我想补充说Clang现在已经“检查算术内置函数”作为语言扩展。 以下是使用checked无符号乘法的示例:

 unsigned x, y, result; ... if (__builtin_umul_overflow(x, y, &result)) { /* overflow occured */ ... } ... 

http://clang.llvm.org/docs/LanguageExtensions.html#checked-arithmetic-builtins

6.2.5第9段是您正在寻找的:

有符号整数类型的非负值范围是相应无符号整数类型的子范围,并且每个类型中相同值的表示是相同的.31)涉及无符号操作数的计算永远不会溢出,因为结果不能由结果无符号整数类型表示的模数减去模数,该数字大于可由结果类型表示的最大值。

我不确定是否有任何编译器开关可用于在C / C ++中强制执行溢出的统一行为。 另一种选择是使用SafeInt模板。 它是一个跨平台的C ++模板,为所有类型的整数操作提供明确的上溢/下溢检查。