Let’s have a look at this code:
int main() { for(int i{0};i<500;i++) { std::cout<<"This is a big number: "<<i*20000000<<std::endl; } }
And let’s compile it with g++ using the flag -O3, what’s going to happen?
Before answering, let’s have a look at the assembly, shall we?
L16: movsbl 39(%ebx), %eax L6: movl %esi, %ecx movl %eax, (%esp) addl $20000000, %edi call __ZNSo3putEc subl $4, %esp movl %eax, %ecx call __ZNSo5flushEv L4: movl $22, 8(%esp) movl $LC2, 4(%esp) movl $__ZSt4cout, (%esp) call __ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_i 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) jne L16 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 subl $4, %esp movsbl %al, %eax jmp L6 L15: call __ZSt16__throw_bad_castv .cfi_endproc
This is actually only a portion of the whole assembly, what I care to show you is the main loop and what’s going on, in the code you can skip the process of calling cout which is somwehere between L4 and the call to L6, and just have a look at the looping condition, there’s something weird there!
Note that jump to L15 is not going to happen (in this particular piece of code), and both jumps to L6 and L16 are not terminating the loop, so where is the loop end condition then?
Well, there’s no end condition, this code will print numbers forever!
If i change the code this way:
int main() { for(int i{0};i<500;i++) { std::cout<<"This is a big number: "<<i*200<<std::endl; } }
The assembler output will be:
L15: movsbl 39(%ebx), %eax L6: movl %edi, %ecx movl %eax, (%esp) addl $200, %esi call __ZNSo3putEc subl $4, %esp movl %eax, %ecx call __ZNSo5flushEv cmpl $100000, %esi //Check whether i==100000, which is 200*500 je L13 //If this is the case, quit the loop L7: movl $22, 8(%esp) movl $LC2, 4(%esp) movl $__ZSt4cout, (%esp) call __ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_i movl %esi, (%esp) movl $__ZSt4cout, %ecx call __ZNSolsEi movl %eax, %edi movl (%eax), %eax subl $4, %esp movl -12(%eax), %eax movl 124(%edi,%eax), %ebx testl %ebx, %ebx je L14 cmpb $0, 28(%ebx) jne L15 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 subl $4, %esp movsbl %al, %eax jmp L6 .p2align 4,,10 L13: //Terminate the loop, quit main leal -16(%ebp), %esp xorl %eax, %eax popl %ecx .cfi_remember_state .cfi_restore 1 .cfi_def_cfa 1, 0 popl %ebx .cfi_restore 3 popl %esi .cfi_restore 6 popl %edi .cfi_restore 7 popl %ebp .cfi_restore 5 leal -4(%ecx), %esp .cfi_def_cfa 4, 4 ret L14: .cfi_restore_state call __ZSt16__throw_bad_castv .cfi_endproc
Most of the code is the same, but now there’s a termination condition and the loop properly exit when the 500 iterations are performed, so the output will be what we’re expecting.
The point of this post is simple, do not ever ignore the warnings your compiler is issuing, many of them may cause serious problems! In many real project indeed, warnings are considered second class citizen, and nobody care about fixing them on-the-fly!
This lead to compilation outputs with hundreds of warnings which from time to time are under the scope, and people are asked to clean the mess up, you know the corporate way: “Fix the warnings as ASAP as possible! Why nobody fixed them before??” said the manager which refused to let people fix them before, no time for that 🙂
Anyway, the problem in this code is the undefined behavior in the expression i*20000000, for i values starting from 108 the multiplication cause an integer overflow, modern compilers will infer that since there’s an undefined behavior which is an unacceptable thing, then it shouldn’t happen at all and the programmer must have taken actions for this situation, therefore it must be the case that i will never be greater than 107, thus the condition i<500 is always true.
Which lead to an infinite loop..
When compiling this code, the compiler will raise the following warning:
undefined_behaviour.cpp: In function 'int main()': undefined_behaviour.cpp:7:48: warning: iteration 108u invokes undefined behavior [-Waggressive-loop-optimizations] std::cout<<"This is a big number: "<<i*20000000<<std::endl; ^ undefined_behaviour.cpp:5:5: note: containing loop for(int i{0};i<500;i++) ^
Which should give us some hint that something is going to be very wrong, we don’t want to have undefined behavior in our code, the fix for the problem (if you don’t want to fix the code) the just compile the program with the flag -fno-aggressive-loop-optimizations.
For details about the original source of information for this problem have a look at HERE, or the related thread on SO.
Always take care of your compiler warnings!
Thanks for reading!