Đoạn mã sau đi vào một vòng lặp vô hạn trên 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;
}
Vì vậy, đây là thỏa thuận: tràn số nguyên đã ký là hành vi không xác định về mặt kỹ thuật. Nhưng GCC trên x86 thực hiện số học số nguyên bằng cách sử dụng các hướng dẫn số nguyên x86 - bao trùm trên tràn.
Do đó, tôi đã mong đợi nó sẽ tràn vào - mặc dù thực tế đó là hành vi không xác định. Nhưng đó rõ ràng không phải là trường hợp. Vậy ... Tôi đã bỏ lỡ gì?
Tôi đã biên dịch cái này bằng cách sử dụng:
~/Desktop$ g++ main.cpp -O2
Đầu ra GCC:
~/Desktop$ ./a.out
536870912
1073741824
-2147483648
0
0
0
... (infinite loop)
Với tối ưu hóa bị vô hiệu hóa, không có vòng lặp vô hạn và đầu ra là chính xác. Visual Studio cũng biên dịch chính xác điều này và cho kết quả như sau:
Đầu ra đúng:
~/Desktop$ g++ main.cpp
~/Desktop$ ./a.out
536870912
1073741824
-2147483648
3
Dưới đây là một số biến thể khác:
i *= 2; // Also fails and goes into infinite loop.
i <<= 1; // This seems okay. It does not enter infinite loop.
Đây là tất cả các thông tin phiên bản có liên quan:
~/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$
Vì vậy, câu hỏi là: Đây có phải là một lỗi trong GCC? Hay tôi đã hiểu nhầm điều gì đó về cách GCC xử lý số học số nguyên?
* Tôi cũng gắn thẻ C này, vì tôi cho rằng lỗi này sẽ sinh sản ở C. (Tôi chưa xác minh nó.)
BIÊN TẬP:
Đây là tập hợp của vòng lặp: (nếu tôi nhận ra nó đúng)
.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
gcc -S
.