Tràn số nguyên đã ký (như nói đúng ra, không có thứ gọi là "tràn số nguyên không dấu") có nghĩa là hành vi không xác định . Và điều này có nghĩa là bất cứ điều gì cũng có thể xảy ra và thảo luận về lý do tại sao nó xảy ra theo các quy tắc của C ++ không có ý nghĩa.
Dự thảo C ++ 11 N3337: §5.4: 1
Nếu trong quá trình đánh giá một biểu thức, kết quả không được định nghĩa về mặt toán học hoặc không nằm trong phạm vi của các giá trị đại diện cho loại của nó, thì hành vi đó không được xác định. [Lưu ý: hầu hết các triển khai C ++ hiện có đều bỏ qua số nguyên trên ows ows. Xử lý phép chia bằng 0, tạo thành phần dư bằng cách sử dụng ước số 0 và tất cả các ngoại lệ điểm khác nhau giữa các máy và thường được điều chỉnh bởi chức năng thư viện. Lưu ý
Mã của bạn được biên dịch với g++ -O3
cảnh báo phát ra (thậm chí không có -Wall
)
a.cpp: In function 'int main()':
a.cpp:11:18: warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations]
std::cout << i*1000000000 << std::endl;
^
a.cpp:9:2: note: containing loop
for (int i = 0; i < 4; ++i)
^
Cách duy nhất chúng ta có thể phân tích những gì chương trình đang làm, là đọc mã lắp ráp được tạo.
Dưới đây là danh sách lắp ráp đầy đủ:
.file "a.cpp"
.section .text$_ZNKSt5ctypeIcE8do_widenEc,"x"
.linkonce discard
.align 2
LCOLDB0:
LHOTB0:
.align 2
.p2align 4,,15
.globl __ZNKSt5ctypeIcE8do_widenEc
.def __ZNKSt5ctypeIcE8do_widenEc; .scl 2; .type 32; .endef
__ZNKSt5ctypeIcE8do_widenEc:
LFB860:
.cfi_startproc
movzbl 4(%esp), %eax
ret $4
.cfi_endproc
LFE860:
LCOLDE0:
LHOTE0:
.section .text.unlikely,"x"
LCOLDB1:
.text
LHOTB1:
.p2align 4,,15
.def ___tcf_0; .scl 3; .type 32; .endef
___tcf_0:
LFB1091:
.cfi_startproc
movl $__ZStL8__ioinit, %ecx
jmp __ZNSt8ios_base4InitD1Ev
.cfi_endproc
LFE1091:
.section .text.unlikely,"x"
LCOLDE1:
.text
LHOTE1:
.def ___main; .scl 2; .type 32; .endef
.section .text.unlikely,"x"
LCOLDB2:
.section .text.startup,"x"
LHOTB2:
.p2align 4,,15
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB1084:
.cfi_startproc
leal 4(%esp), %ecx
.cfi_def_cfa 1, 0
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
.cfi_escape 0x10,0x5,0x2,0x75,0
movl %esp, %ebp
pushl %edi
pushl %esi
pushl %ebx
pushl %ecx
.cfi_escape 0xf,0x3,0x75,0x70,0x6
.cfi_escape 0x10,0x7,0x2,0x75,0x7c
.cfi_escape 0x10,0x6,0x2,0x75,0x78
.cfi_escape 0x10,0x3,0x2,0x75,0x74
xorl %edi, %edi
subl $24, %esp
call ___main
L4:
movl %edi, (%esp)
movl $__ZSt4cout, %ecx
call __ZNSolsEi
movl %eax, %esi
movl (%eax), %eax
subl $4, %esp
movl -12(%eax), %eax
movl 124(%esi,%eax), %ebx
testl %ebx, %ebx
je L15
cmpb $0, 28(%ebx)
je L5
movsbl 39(%ebx), %eax
L6:
movl %esi, %ecx
movl %eax, (%esp)
addl $1000000000, %edi
call __ZNSo3putEc
subl $4, %esp
movl %eax, %ecx
call __ZNSo5flushEv
jmp L4
.p2align 4,,10
L5:
movl %ebx, %ecx
call __ZNKSt5ctypeIcE13_M_widen_initEv
movl (%ebx), %eax
movl 24(%eax), %edx
movl $10, %eax
cmpl $__ZNKSt5ctypeIcE8do_widenEc, %edx
je L6
movl $10, (%esp)
movl %ebx, %ecx
call *%edx
movsbl %al, %eax
pushl %edx
jmp L6
L15:
call __ZSt16__throw_bad_castv
.cfi_endproc
LFE1084:
.section .text.unlikely,"x"
LCOLDE2:
.section .text.startup,"x"
LHOTE2:
.section .text.unlikely,"x"
LCOLDB3:
.section .text.startup,"x"
LHOTB3:
.p2align 4,,15
.def __GLOBAL__sub_I_main; .scl 3; .type 32; .endef
__GLOBAL__sub_I_main:
LFB1092:
.cfi_startproc
subl $28, %esp
.cfi_def_cfa_offset 32
movl $__ZStL8__ioinit, %ecx
call __ZNSt8ios_base4InitC1Ev
movl $___tcf_0, (%esp)
call _atexit
addl $28, %esp
.cfi_def_cfa_offset 4
ret
.cfi_endproc
LFE1092:
.section .text.unlikely,"x"
LCOLDE3:
.section .text.startup,"x"
LHOTE3:
.section .ctors,"w"
.align 4
.long __GLOBAL__sub_I_main
.lcomm __ZStL8__ioinit,1,1
.ident "GCC: (i686-posix-dwarf-rev1, Built by MinGW-W64 project) 4.9.0"
.def __ZNSt8ios_base4InitD1Ev; .scl 2; .type 32; .endef
.def __ZNSolsEi; .scl 2; .type 32; .endef
.def __ZNSo3putEc; .scl 2; .type 32; .endef
.def __ZNSo5flushEv; .scl 2; .type 32; .endef
.def __ZNKSt5ctypeIcE13_M_widen_initEv; .scl 2; .type 32; .endef
.def __ZSt16__throw_bad_castv; .scl 2; .type 32; .endef
.def __ZNSt8ios_base4InitC1Ev; .scl 2; .type 32; .endef
.def _atexit; .scl 2; .type 32; .endef
Tôi hầu như không thể đọc lắp ráp, nhưng thậm chí tôi có thể nhìn thấy addl $1000000000, %edi
dòng. Mã kết quả trông giống như
for(int i = 0; /* nothing, that is - infinite loop */; i += 1000000000)
std::cout << i << std::endl;
Nhận xét này của @TC:
Tôi nghi ngờ rằng đó là một cái gì đó như: (1) bởi vì mỗi lần lặp với i
bất kỳ giá trị nào lớn hơn 2 đều có hành vi không xác định -> (2) chúng ta có thể giả sử rằng i <= 2
vì mục đích tối ưu hóa -> (3) điều kiện vòng lặp luôn luôn đúng -> (4 ) nó được tối ưu hóa thành một vòng lặp vô hạn.
đã cho tôi ý tưởng để so sánh mã lắp ráp của mã OP với mã lắp ráp của mã sau đây, không có hành vi không xác định.
#include <iostream>
int main()
{
// changed the termination condition
for (int i = 0; i < 3; ++i)
std::cout << i*1000000000 << std::endl;
}
Và, trên thực tế, mã chính xác có điều kiện chấm dứt.
; ...snip...
L6:
mov ecx, edi
mov DWORD PTR [esp], eax
add esi, 1000000000
call __ZNSo3putEc
sub esp, 4
mov ecx, eax
call __ZNSo5flushEv
cmp esi, -1294967296 // here it is
jne L7
lea esp, [ebp-16]
xor eax, eax
pop ecx
; ...snip...
OMG, điều đó hoàn toàn không rõ ràng! Thật không công bằng! Tôi yêu cầu thử nghiệm bằng lửa!
Đối phó với nó, bạn đã viết mã lỗi và bạn sẽ cảm thấy tồi tệ. Chịu hậu quả.
... Hoặc, thay vào đó, sử dụng hợp lý các chẩn đoán tốt hơn và các công cụ sửa lỗi tốt hơn - đó là những gì họ dành cho:
Tôi có một mớ hỗn độn của một chương trình không phải do tôi viết mà cần phải chuyển đi vào ngày mai! GIÚP !!!!!!
Sử dụng gcc -fwrapv
Tùy chọn này hướng dẫn trình biên dịch giả định rằng tràn số học đã ký của phép cộng, phép trừ và phép nhân kết thúc bằng cách sử dụng biểu diễn bổ sung twos.
1 - quy tắc này không áp dụng cho "tràn số nguyên không dấu", như §3.9.1.4 nói rằng
Các số nguyên không dấu, được khai báo không dấu, sẽ tuân theo các định luật modulo số học 2 n trong đó n là số bit trong biểu diễn giá trị của kích thước cụ thể đó của số nguyên.
và ví dụ kết quả của UINT_MAX + 1
được xác định theo toán học - theo quy tắc modulo số học 2 n