【问题标题】:Why does integer overflow work differently inside of if statement conditional?为什么整数溢出在 if 语句条件中的工作方式不同?
【发布时间】:2017-09-20 22:39:49
【问题描述】:

在下面的代码中,x 和 y 是 int32_t 变量。在这个简化的例子中,它们总是相差 1。当它们跨越 int32_t 溢出边界(0x7FFFFFFF,最大 2 的补码 32 位正数,到 0x80000000,最大幅度负数)时,减去它们似乎会给出不同的结果在 if 语句的条件内完成(方法 1),而不是将结果存储在临时变量中(方法 2)。为什么他们不给出相同的结果?

我认为减去两个 int32_t 变量会产生 int32_t 类型的结果,因此使用该类型的临时变量不会改变任何内容。我尝试在 if 语句中有条件地进行显式类型转换;这并没有改变任何东西。 FWIW,方法 2 给出了我期望的结果。

代码:

int32_t x = (0x80000000 - 3);

int i;
for( i = 0; i < 5; ++i )
{
    int32_t y = x + 1;      // this may cause rollover from 0x7fffffff (positive) to 0x80000000 (negative)

    UARTprintf("\n" "x = 0x%08X, y = 0x%08X", x, y );

    if( ( y - x ) >= 1 )            // Method 1
        UARTprintf(" - true ");
    else
        UARTprintf(" - FALSE");

    int32_t z = ( y - x );          // Method 2
    if( ( z ) >= 1 )
        UARTprintf(" - true ");
    else
        UARTprintf(" - false");

    ++x;
}

输出:

x = 0x7ffffffd, y = 0x7ffffffe - true  - true
x = 0x7ffffffe, y = 0x7fffffff - true  - true
x = 0x7fffffff, y = 0x80000000 - FALSE - true
x = 0x80000000, y = 0x80000001 - true  - true
x = 0x80000001, y = 0x80000002 - true  - true

在我的实际应用程序中(不是这个简化的示例),y 由硬件计时器递增,x 是最后一次执行某些代码的时间的记录。该测试旨在使一些代码间隔运行。考虑到 y 代表时间,并且应用程序在重新启动之前可能会运行很长时间,所以不能让它溢出。

请注意,正如你们中的一些人所做的那样,标准没有定义有符号整数溢出发生时的行为告诉我,我无权抱怨我不能指望它按我想要的方式工作到,但它没有给我一个我可以依靠的解决方案。即使使用似乎适用于我当前编译器版本和设置的临时变量,当其中一件事情发生变化时,它也可能会停止工作。除了求助于汇编代码,我还有什么值得信赖的选择吗?

【问题讨论】:

  • 请注意,有符号整数溢出会导致未定义的行为。
  • 顺便说一句ideone.com/1uxeDp。您是否进行了优化编译?
  • 请说明您使用的是 C99 模式还是 C11 模式(相对于 C89 模式或专有模式,具有非标准定义 int32_t
  • 除了溢出 UB,您还会在 printf 中使用错误的格式说明符导致 UB(假设 UARTprintf 是标准 printf 函数的宏,或者最终是 va_arg(ap, unsigned int))跨度>
  • @Olaf 我怀疑 OP 在选择标签时阅读并遵循标签 wiki 政策

标签: c if-statement integer-overflow c89


【解决方案1】:

鉴于有符号整数溢出会导致未定义的行为 - 你最好不要试图解释它。

因为您的假设是基于“常识”,而不是标准。

否则 - 检查程序集并尝试调试它,但同样,结果将无法扩展:您将无法将新知识应用于其他情况(但毫无疑问,这样做会很有趣) .

【讨论】:

    【解决方案2】:

    我最初不知道要问的问题是,“在对可能溢出的整数进行减法时,如何避免未定义的行为?”如果我错了,请纠正我,但该问题的答案似乎是“使用无符号整数而不是有符号整数”,因为结果定义明确(根据 C11 6.2.5/9)“无法表示的结果生成的无符号整数类型以比生成的类型可以表示的最大值大一的数字为模减少。"

    在这种情况下,这足以提出一个可行的解决方案,因为经过的时间将始终为零或一个小的正数,因此减法的结果将始终为正。因此减法的结果可以保存为无符号数并进行比较 (">=1" ) 或转换回有符号 int 进行比较 (C11 6.3.1.3 "当整数类型的值转换为另一种整数类型时。 ..如果该值可以由新类型表示,则它不变。” 此代码有效,我相信不依赖任何未定义的行为:“if( ( (int32_t)(uint32_t)y - (uint32_t)x ) >= 1)"

    然而,在更一般的情况下,转换为无符号以进行减法,然后转换回有符号的结果(可能为负数)并没有明确定义。 C11 6.3.1.3 说关于转换为另一种整数类型,如果“新类型是有符号的并且值不能在其中表示;结果是实现定义的,或者引发了实现定义的信号。”所以我仍然可以想象一个需要汇编代码来实现定义明确的结果的场景;这不是其中之一。

    【讨论】:

    • 如果使用 C++,看起来一个实际的解决方案是使用像 SafeInt safeint.codeplex.com> 这样的库。在某些情况下,它会“使用无符号数进行中间计算,并且标准定义了无符号溢出……”例如,“编译器可能会删除 -x,但不会删除 ~(unsigned)x + 1,它发出相同的位模式(和相同的汇编代码)。”
    猜你喜欢
    • 2012-02-15
    • 1970-01-01
    • 2021-08-13
    • 2019-11-26
    • 1970-01-01
    • 1970-01-01
    • 2015-03-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多