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

发布于 2024-09-19 07:17:28 字数 407 浏览 7 评论 0原文

感谢 Carl Norum 的帮助,进行了编辑以包含正确的标准参考。

C 标准声明

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

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

为了唯一起见,我们将标准设为C99,编译器设为gcc。但我对其他编译器(icc、cl)和其他标准(C1x、C89)的答案感兴趣。事实上,只是为了惹恼 C/C++ 人群,我什至希望得到 C++0x、C++03 和 C++98 的答案。

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

Edited to include proper standard reference thanks to Carl Norum.

The C standard states

If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.

Are there compiler switches that guarantee certain behaviors on integer overflow? I'd like to avoid nasal demons. In particular, I'd like to force the compiler to wrap on overflow.

For the sake of uniqueness, let's take the standard to be C99 and the compiler to be gcc. But I would be interested in answers for other compilers (icc, cl) and other standards (C1x, C89). In fact, just to annoy the C/C++ crowd, I'd even appreciate answers for C++0x, C++03, and C++98.

Note: International standard ISO/IEC 10967-1 may be relevant here, but as far as I could tell it was mentioned only in the informative annex.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

最佳男配角 2024-09-26 07:17:28

看看 -ftrapv和<代码>-fwrapv

-ftrapv

此选项生成加法、减法、乘法运算有符号溢出的陷阱。

-fwrapv

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

Take a look at -ftrapv and -fwrapv:

-ftrapv

This option generates traps for signed overflow on addition, subtraction, multiplication operations.

-fwrapv

This option instructs the compiler to assume that signed arithmetic overflow of addition, subtraction and multiplication wraps around using twos-complement representation. This flag enables some optimizations and disables other. This option is enabled by default for the Java front-end, as required by the Java language specification.

骄傲 2024-09-26 07:17:28

对于您的 C99 答案,我认为 6.5 表达式,第 5 段就是您要寻找的内容:

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

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

涉及无符号操作数的计算永远不会溢出,因为无法由结果无符号整数类型表示的结果会以比结果类型可以表示的最大值大一的数为模进行缩减。

C++ 有相同的语句,但措辞略有不同:

  • 5 个表达式,第 4 段:

    <块引用>

    如果在计算表达式期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为未定义。 [注意:大多数现有的 C++ 实现都会忽略整数溢出。除以零的处理、使用零除数形成余数以及所有浮点异常因机器而异,并且通常可以通过库函数进行调整。 —尾注]

  • 3.9.1 基本类型,第 4 段:

    <块引用>

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

For your C99 answer, I think 6.5 Expressions, paragraph 5 is what you're looking for:

If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined.

That means if you get an overflow, you're out of luck - no behaviour of any kind guaranteed. Unsigned types are a special case, and never overflow (6.2.5 Types, paragraph 9):

A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.

C++ has the same statements, worded a bit differently:

  • 5 Expressions, paragraph 4:

    If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined. [Note: most existing implementations of C++ ignore integer overflows. Treatment of division by zero, forming a remainder using a zero divisor, and all floating point exceptions vary among machines, and is usually adjustable by a library function. —endnote]

  • 3.9.1 Fundamental types, paragraph 4:

    Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2^n where n is the number of bits in the value representation of that particular size of integer.

三生路 2024-09-26 07:17:28

在 C99 中,一般行为在 6.5/5 中描述

如果发生异常情况
在计算表达式时
(也就是说,如果结果不是
数学定义或不在
其可表示值的范围
类型),行为未定义。

无符号类型的行为在 6.2.5/9 中描述,它基本上指出对无符号类型的操作永远不会导致异常情况

涉及无符号的计算
操作数永远不会溢出,因为
无法表示的结果
结果无符号整数类型是
对 1 进行模减
大于最大值
可以用结果表示
类型。

GCC 编译器有一个特殊选项 -ftrapv,旨在捕获有符号整数运算的运行时溢出。

In C99 the general behavior is desribed in 6.5/5

If an exceptional condition occurs
during the evaluation of an expression
(that is, if the result is not
mathematically defined or not in the
range of representable values for its
type), the behavior is undefined.

The behavior of unsigned types is described in 6.2.5/9, which basically states that operations on unsigned types never lead to exceptional condition

A computation involving unsigned
operands can never overflow, because a
result that cannot be represented by
the resulting unsigned integer type is
reduced modulo the number that is one
greater than the largest value that
can be represented by the resulting
type.

GCC compiler has a special option -ftrapv, which is intended to catch run-time overflow of signed integer operations.

一梦浮鱼 2024-09-26 07:17:28

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

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

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

For completeness, I'd like to add that Clang now has "checked arithmetic builtins" as a language extension. Here is an example using checked unsigned multiplication:

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

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

温柔嚣张 2024-09-26 07:17:28

之前的帖子都评论了C99标准,但实际上这个保证很早就有了。

第 6.1.2.5 节类型第 5 段规定

C89 标准

涉及无符号操作数的计算永远不会溢出,
因为结果无法用结果无符号表示
整数类型以大于 1 的数字为模进行缩减
生成的无符号整数类型可以表示的最大值。

请注意,这允许 C 程序员将所有无符号除法替换为某个常量,然后用 C 的模 2^N 区间算术形成的环的逆元素的乘法来替换。

并且这可以在没有任何“校正”的情况下完成,因为需要通过与倒数值的定点乘法来近似除法。

相反,可以使用扩展欧几里得算法来查找逆元素并将其用作乘数。 (当然,为了保持可移植性,还应该应用按位 AND 运算,以确保结果具有相同的位宽度。)

可能值得评论的是,大多数 C 编译器已经将其实现为优化。然而,此类优化并不能得到保证,因此,在速度很重要但 C 优化器的功能未知或特别弱的情况下,程序员可能仍然会感兴趣手动执行此类优化。

最后一点,尝试这样做的原因是:用于乘法的机器级指令通常比用于除法的指令快得多,尤其是在高性能 CPU 上。

The previous postings all commented on the C99 standard, but in fact this guarantee was already available earlier.

The 5th paragraph of Section 6.1.2.5 Types

of the C89 standard states

A computation involving unsigned operands can never overflow,
because a result that cannot be represented by the resulting unsigned
integer type is reduced modulo the number that is one greater than
the largest value that can be represented by the resulting unsigned integer type.

Note that this allows C programmers to replace all unsigned divisions by some constant to be replaced by a multiplication with the inverse element of the ring formed by C's modulo 2^N interval arithmetic.

And this can be done without any "correction" as it would be necessary by approximating the division with a fixed-point multiplication with the reciprocal value.

Instead, the Extended Euclidian Algorithm can be used to find the inverse Element and use it as the multiplier. (Of course, for the sake of staying portable, bitwise AND operations should also be applied in order to ensure the results have the same bit widths.)

It may be worthwhile to comment that most C compilers already implement this as an optimization. However, such optimizations are not guaranteed, and therefore it might still be interesting for programmers to perform such optimizations manually in situations where speed matters, but the capabilities of the C optimizer are either unknown or particularly weak.

And as a final remark, the reason for why trying to do so at all: The machine-level instructions for multiplication are typically much faster than those for division, especially on high-performance CPUs.

月野兔 2024-09-26 07:17:28

6.2.5 第 9 段是您要查找的内容:

有符号整数类型的非负值范围是
对应的无符号整数类型,并且每个中相同值的表示
类型是相同的。31)涉及无符号操作数的计算永远不会溢出,
因为无法用结果无符号整数类型表示的结果是
减少对比最大值大 1 的数取模
由结果类型表示。

6.2.5 paragraph 9 is what you're looking for:

The range of nonnegative values of a signed integer type is a subrange of the
corresponding unsigned integer type, and the representation of the same value in each
type is the same.31) A computation involving unsigned operands can never overflow,
because a result that cannot be represented by the resulting unsigned integer type is
reduced modulo the number that is one greater than the largest value that can be
represented by the resulting type.

白首有我共你 2024-09-26 07:17:28

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

I'm not sure if there are any compiler switches you can use to enforce uniform behavior for overflows in C/C++. Another option is to use the SafeInt<T> template. It's a cross platform C++ template that provides definitive overflow / underflow checks for all types of integer operations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文