【问题标题】:C integer overflowC整数溢出
【发布时间】:2020-10-22 06:03:01
【问题描述】:

我在 C 中处理整数,试图更多地探索溢出发生的时间和方式。

我注意到当我将两个正数相加时,总和溢出,我总是得到一个负数。

另一方面,如果我将两个负数相加,其和溢出,我总是得到一个正数(包括 0)。

我做了一些实验,但我想知道这是否适用于每种情况。

【问题讨论】:

  • 下溢和上溢对于有符号整数是未定义的,但对于无符号整数是明确定义的。假设您添加的那些正值不是无符号的,那么可能会发生任何事情。
  • 虽然“未定义”,但几乎每台现代计算机都使用“2 的补码”算术,因此出于实际目的,它被定义并按照您的想法工作,除了少数“边界”我现在无法理解的情况。
  • @HotLicks,你让我想起了this C++ question,那里发生了一个异常。
  • @chris -- 当然,在那种情况下,算术确实(显然)换行了,只是编译器假设算术不会传递到“未定义”区域,因此优化了测试. “未定义”规则用于允许优化,如果包装行为在规范中是不可能的。
  • @HotLicks,这就是为什么 C++ 中的 UB 是一个的东西:D

标签: c integer overflow


【解决方案1】:

C 中的溢出是一团糟。

  • 在无符号算术或转换为无符号类型期间溢出导致模 2 换行n
  • 转换为有符号类型期间的溢出是由实现定义的,大多数实现会包装模 2n,但有些可能不会。
  • 有符号算术期间的溢出是未定义的行为,根据标准,任何事情都可能发生。在实践中,有时它会做你不想做的事情,有时它会在编译器优化重要测试时在你的代码中导致奇怪的问题。

让事情变得更糟的是它如何与整数提升相互作用。多亏了提升,当看起来你正在做无符号算术时,你可以做有符号算术。例如考虑以下代码

uint16_t a = 65535;
uint16_t b = a * a;

在具有 16 位 int 的系统上,此代码是明确定义的。然而,在具有 32 位 int 的系统上,乘法将作为有符号 int 进行,由此产生的溢出将是未定义的行为!

【讨论】:

    【解决方案2】:

    整数溢出是 C 中未定义的行为。

    C 表示一个涉及整数的表达式溢出,如果它在通常的算术转换之后的结果是有符号类型的并且不能以结果的类型表示。赋值和强制转换表达式是一个例外,因为它们是由整数转换决定的。

    无符号类型的表达式不能溢出,它们会换行,例如。例如,0U - 1UINT_MAX

    例子:

    INT_MAX + 1    // integer overflow
    UINT_MAX + 1   // no overflow, the resulting type is unsigned
    (unsigned char) INT_MAX // no overflow, integer conversion occurs 
    

    永远不要让任何整数表达式溢出,现代编译器(如gcc)利用整数溢出这一未定义行为来执行各种类型的优化。

    例如:

    a - 10 < 20
    

    a 升级后的类型为int 时,gcc 中的表达式(启用优化时)减少为:

    a < 30
    

    aINT_MIN + 10 - 1INT_MIN 范围内时,它利用了表达式未定义的行为。

    aunsigned int 时无法进行此优化,因为如果a0,则必须将a - 10 评估为UINT_MAX - 9(没有未定义的行为)。当a09 时,将a - 10 &lt; 20 优化到a &lt; 30 会导致与所需结果不同的结果。

    【讨论】:

      【解决方案3】:

      形式上,有符号算术溢出的行为是未定义的;任何事情都可能发生,而且它是“正确的”。这与完全定义溢出的无符号算术形成对比。

      实际上,许多较旧的编译器使用有符号算术,如您所描述的那样溢出。然而,现代 GCC 正在改变它的工作方式,你会非常不明智地依赖这种行为。当编译代码的环境中的任何东西发生变化时,它可能随时发生变化——编译器、平台……

      【讨论】:

      • 呵呵,你有 GCC 变化的参考吗?我有兴趣读一下。
      • 不具体,但如果您在 comp.std.ccomp.lang.ccomp.lang.c.moderated 新闻组中看到讨论,您会发现一些更改的引用 — 至少编译器可以确定的位置(必须在编译时)会发生溢出。
      • 听起来对我有用。如果 C++ 新手碰巧获得了更新版本的 GCC,这应该可以为他们节省一些麻烦。我不认为 4.8 或任何东西会在一段时间内出现,或者它将包含在哪个版本中。
      【解决方案4】:

      有符号整数的溢出是 C 中未定义的行为,因此无法保证。

      也就是说,环绕,或算术模 2N,其中N 是类型中的位数,是一种常见的行为。对于这种行为,确实如果总和溢出,则结果与操作数的符号相反。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-07
        • 2013-04-10
        • 2022-01-21
        • 2021-03-15
        • 1970-01-01
        相关资源
        最近更新 更多