Un débordement d'entier signé (à proprement parler, il n'y a pas de "débordement d'entier non signé") signifie un comportement indéfini . Et cela signifie que tout peut arriver, et discuter de pourquoi cela se produit selon les règles de C ++ n'a pas de sens.
Projet C ++ 11 N3337: §5.4: 1
Si lors de l'évaluation d'une expression, le résultat n'est pas défini mathématiquement ou n'est pas dans la plage de valeurs représentables de son type, le comportement est indéfini. [Remarque: la plupart des implémentations existantes de C ++ ignorent les débordements d'entiers. Le traitement de la division par zéro, formant un reste en utilisant un diviseur nul, et toutes les exceptions de point flottant varient selon les machines et est généralement ajustable par une fonction de bibliothèque. —End note]
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 d'analyser ce que fait le programme est de lire le code d'assemblage généré.
Voici la liste complète de l'assemblage:
.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 l'assemblage, mais même je peux voir la addl $1000000000, %edi
ligne. 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 @TC:
Je soupçonne que c'est quelque chose comme: (1) parce que chaque itération avec i
une valeur supérieure à 2 a un comportement non défini -> (2) nous pouvons supposer qu'à i <= 2
des fins d'optimisation -> (3) la condition de boucle est toujours vraie -> (4 ) il est optimisé dans une boucle infinie.
m'a donné l'idée de comparer le code d'assemblage du code de l'OP au code d'assemblage du code suivant, sans 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 une condition de terminaison.
; ...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, ce n'est pas du tout évident! Ce n'est pas juste! J'exige un procès par le feu!
Traitez-le, vous avez écrit le code du buggy et vous devriez vous sentir mal. Supportez les conséquences.
... ou, alternativement, utilisez correctement de meilleurs diagnostics et de meilleurs outils de débogage - c'est à cela qu'ils servent:
J'ai un désordre spaghetti d'un programme non écrit par moi qui doit être expédié demain! AIDE !!!!!! 111oneone
Utilisez gcc -fwrapv
Cette option demande au compilateur de supposer que le débordement arithmétique signé d'addition, de soustraction et de multiplication s'enroule en utilisant la représentation à complément à deux.
1 - cette règle ne s'applique pas au "débordement d'entier non signé", comme le §3.9.1.4 dit que
Les entiers non signés, déclarés non signés, doivent obéir aux lois de l'arithmétique modulo 2 n où n est le nombre de bits dans la représentation de valeur de cette taille particulière d'entier.
et par exemple le résultat de UINT_MAX + 1
est défini mathématiquement - par les règles de l'arithmétique modulo 2 n