由Carl Norum编辑,包括适当的标准参考。
C标准规定
如果在计算表达式期间发生异常情况(即,如果结果未在数学上定义或未在其类型的可表示值范围内),则行为未定义。
是否有编译器开关可以保证整数溢出时的某些行为?我想避免鼻腔恶魔。特别是,我想强制编译器包装溢出。
为了唯一性,让标准为C99,编译器为gcc。但我会对其他编译器(icc,cl)和其他标准(C1x,C89)的答案感兴趣。事实上,只是为了惹恼C / C ++人群,我甚至欣赏C ++ 0x,C ++ 03和C ++ 98的答案。
注:国际标准ISO / IEC 10967-1可能与此相关,但据我所知,仅在资料性附录中提到过。
-ftrapy
此选项为加法,减法,乘法运算的带符号溢出生成陷阱。
-forapy
此选项指示编译器假设加法,减法和乘法的带符号算术溢出使用二进制补码表示。此标志启用一些优化并禁用其他优化。默认情况下,Java前端根据Java语言规范的要求启用此选项。
对于你的C99答案,我认为6.5表达式,第5段是你正在寻找的:
如果在计算表达式期间发生异常情况(即,如果结果未在数学上定义或未在其类型的可表示值范围内),则行为未定义。
这意味着如果你遇到溢出,你就不走运了 - 没有任何保证的行为。无符号类型是一种特殊情况,永不溢出(6.2.5类型,第9段):
涉及无符号操作数的计算永远不会溢出,因为无法通过生成的无符号整数类型表示的结果将以比结果类型可以表示的最大值大1的数量为模。
C ++有相同的陈述,措辞有点不同:
unsigned
,应遵守算术模2 ^ n的定律,其中n是该特定整数大小的值表示中的位数。在C99中,一般行为在6.5 / 5中描述
如果在计算表达式期间发生异常情况(即,如果结果未在数学上定义或未在其类型的可表示值范围内),则行为未定义。
无符号类型的行为在6.2.5 / 9中描述,它基本上表明对无符号类型的操作永远不会导致异常条件
涉及无符号操作数的计算永远不会溢出,因为无法通过生成的无符号整数类型表示的结果将以比结果类型可以表示的最大值大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)涉及无符号操作数的计算永远不会溢出,因为结果不能由结果无符号整数类型表示的模数减去模数,该数字大于可由结果类型表示的最大值。
之前的帖子都对C99标准进行了评论,但实际上这种保证早已可用。
第6.1.2.5节类型的第5段
C89标准的状态
涉及无符号操作数的计算永远不会溢出,因为无法用结果无符号整数类型表示的结果以比模式生成的无符号整数类型所表示的最大值大1的数量减少。
注意,这允许C程序员用一些常数替换所有无符号除法,用C的模2 ^ N区间算法形成的环的逆元素乘以。
并且这可以在没有任何“校正”的情况下完成,因为通过使用具有倒数值的定点乘法来近似除法将是必要的。
相反,扩展欧几里德算法可用于查找逆元素并将其用作乘数。 (当然,为了保持便携性,还应该使用按位AND运算,以确保结果具有相同的位宽。)
值得评论的是,大多数C编译器已将其作为优化实现。但是,这样的优化并不能得到保证,因此程序员在速度很重要的情况下手动执行这种优化仍然很有趣,但C优化器的功能要么是未知的,要么特别弱。
作为最后的评论,为什么要尝试这样做的原因:乘法的机器级指令通常比分区的指令快得多,特别是在高性能CPU上。
我不确定是否有任何编译器开关可用于强制执行C / C ++中溢出的统一行为。另一种选择是使用SafeInt<T>
模板。它是一个跨平台的C ++模板,为所有类型的整数操作提供明确的上溢/下溢检查。