为什么x86上的整数溢出会导致无限循环?

以下代码在GCC上进入无限循环:

#include <iostream>
using namespace std;

int main(){
    int i = 0x10000000;

    int c = 0;
    do{
        c++;
        i += i;
        cout << i << endl;
    }while (i > 0);

    cout << c << endl;
    return 0;
}

所以这里是交易:有符号的整数溢出在技术上是未定义的行为。 但x86上的GCC使用x86整数指令实现整数运算 - 在溢出时换行。

因此,我预料它会在溢出时进行换行 - 尽管它是未定义的行为。 但事实并非如此。 那么我错过了什么?

我编译这个使用:

~/Desktop$ g++ main.cpp -O2

GCC输出:

~/Desktop$ ./a.out
536870912
1073741824
-2147483648
0
0
0

... (infinite loop)

禁用优化后,不会出现无限循环并且输出正确。 Visual Studio也正确编译这个并给出以下结果:

正确的输出:

~/Desktop$ g++ main.cpp
~/Desktop$ ./a.out
536870912
1073741824
-2147483648
3

以下是其他一些变体:

i *= 2;   //  Also fails and goes into infinite loop.
i <<= 1;  //  This seems okay. It does not enter infinite loop.

以下是所有相关的版本信息:

~/Desktop$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ..

...

Thread model: posix
gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4) 
~/Desktop$ 

所以问题是:这是GCC中的错误吗? 还是我误解了GCC如何处理整数算术?

*我也在标记这个C,因为我认为这个bug会在C中重现(我还没有验证过)。

编辑:

这里是循环的组装:(如果我正确地认识到)

.L5:
addl    %ebp, %ebp
movl    $_ZSt4cout, %edi
movl    %ebp, %esi
.cfi_offset 3, -40
call    _ZNSolsEi
movq    %rax, %rbx
movq    (%rax), %rax
movq    -24(%rax), %rax
movq    240(%rbx,%rax), %r13
testq   %r13, %r13
je  .L10
cmpb    $0, 56(%r13)
je  .L3
movzbl  67(%r13), %eax
.L4:
movsbl  %al, %esi
movq    %rbx, %rdi
addl    $1, %r12d
call    _ZNSo3putEc
movq    %rax, %rdi
call    _ZNSo5flushEv
cmpl    $3, %r12d
jne .L5

当标准说它是未定义的行为时,就意味着它。 任何事情都可能发生。 “任何事物”都包括“通常是整数,但偶尔会发生奇怪的事情”。

是的,在x86 CPU上,整数通常会按照您期望的方式包装。 这是这些例外之一。 编译器假定你不会导致未定义的行为,并优化掉循环测试。 如果你真的想环绕,在编译时传递-fwrapvg++gcc ; 这会给你定义好的(二进制补码)溢出语义,但会伤害性能。


这很简单:未定义的行为 - 尤其是优化( -O2 )打开 - 意味着任何事情都可能发生。

您的代码表现为(您)没有-O2开关的期望。

顺便说一下,icl和tcc的效果很好,但你不能依赖这样的东西......

据此,gcc优化实际上利用了有符号的整数溢出。 这意味着“错误”是由设计决定的。


这里需要注意的重要一点是C ++程序是为C ++抽象机器编写的(通常通过硬件指令来模拟)。 您正在编译x86的事实与这种未定义的行为完全无关。

编译器可以自由使用未定义行为的存在来改进其优化(通过从循环中删除条件,如本例中所示)。 除了要求机器代码在执行时产生C ++抽象机器所要求的结果之外,在C ++级别构造和x86级别机器代码构造之间没有保证或甚至是有用的映射。

链接地址: http://www.djcxy.com/p/84447.html

上一篇: Why does integer overflow on x86 with GCC cause an infinite loop?

下一篇: Variable Definition Ignore in C