Signé débordement d'entier (comme strictement parlant, il n'y a pas une telle chose comme "unsigned integer overflow") signifie un comportement indéterminé. Et cela signifie que tout peut arriver, et de discuter de pourquoi faut-il se produire dans les règles de C++ n'a pas de sens.
C++11 projet de N3337: §5.4:1
Si, lors de l'évaluation d'une expression, le résultat n'est pas mathématiquement définie ou non dans la gamme de
représentable valeurs pour son type, le comportement est indéfini. [ Note: la plupart des implémentations existantes de C++
ignorer les débordements d'entiers. Traitement de division par zéro, formant un reste à l'aide d'un diviseur de zéro, et tous les
virgule flottante exceptions varient entre machines, et est généralement réglable par une fonction de la bibliothèque. -la note de fin ]
Votre code compilé avec g++ -O3
émet un avertissement (même sans -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)
^
La seule façon que nous pouvons analyser ce que le programme est en train de faire, c'est de lire le code assembleur généré.
Voici l'assemblage complet d'inscription:
.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
Je peux à peine lire même assemblée, mais même moi, je peux voir l' addl $1000000000, %edi
ligne de.
Le code résultant ressemble plus à
for(int i = 0; /* nothing, that is - infinite loop */; i += 1000000000)
std::cout << i << std::endl;
Ce commentaire de @T. C.:
Je soupçonne que c'est quelque chose comme: (1) parce qu'à chaque itération avec i
de la valeur de plus de 2 a un comportement indéfini -> (2), on peut supposer qu' i <= 2
pour des raisons d'optimisation -> (3) la condition de la boucle est toujours vrai -> (4) il est optimisé à l'écart dans une boucle infinie.
m'a donné l'idée de comparer l'assemblée du code de l'OP du code de l'assemblée de code de le code suivant, avec aucun comportement indéfini.
#include <iostream>
int main()
{
// changed the termination condition
for (int i = 0; i < 3; ++i)
std::cout << i*1000000000 << std::endl;
}
Et, en fait, le code correct a condition de résiliation.
; ...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, c'est pas du tout évident! Ce n'est pas juste! Je demande à l'épreuve du feu!
Traiter avec elle, vous avez écrit le code bogué et vous devriez vous sentir mal. En supporter les conséquences.
...ou, sinon, de faire bon usage de l'amélioration des diagnostics et des meilleurs outils de débogage - c'est ce qu'ils sont pour:
J'ai un spaghetti désordre d'un programme n'est pas écrit par moi qui doit être livré demain! AIDER!!!!!!111oneone
L'utilisation de gcc -fwrapv
Cette option indique au compilateur de supposer que signée de dépassement de capacité arithmétique d'addition, de soustraction et de multiplication s'enroule autour de l'aide de deux-complément de la représentation.
1 - cette règle ne s'applique pas aux "unsigned integer overflow", §3.9.1.4 dit que
Des entiers non signés, déclaré non signé, doit obéir aux lois de l'arithmétique modulo 2n où n est le nombre
de bits dans la représentation de la valeur de la taille de l'entier.
et par exemple est le résultat de l' UINT_MAX + 1
est définie mathématiquement par les règles de l'arithmétique modulo 2n