下面的代码在 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
海湾合作委员会产出:
~/Desktop$ ./a.out
536870912
1073741824
-2147483648
0
0
0
... (infinite loop)
禁用优化后,就不存在无限循环,输出正确。VisualStudio 也正确地编译了这些代码,并得到以下结果:
正确输出:
~/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$
所以问题是: 这是海湾合作委员会的一个 bug 吗?还是我对 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