【发布时间】: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