I have the following code:
#include <iostream> #include <complex> using namespace std; int main() { complex<int> delta; complex<int> mc[4] = {0}; for(int di = 0; di < 4; di++, delta = mc[di]) { cout << di << endl; } return 0; }
I expect it to output "0, 1, 2, 3" and stop, but it outputs an endless series of "0, 1, 2, 3, 4, 5, ....."
It looks like the comparison di<4
doesn't work well and always returns true.
If I just comment out ,delta=mc[di]
, I get "0, 1, 2, 3" as normal. What's the problem with the innocent assignment?
I am using Ideone.com g++ C++14 with -O2 option.
Compilation error refers to a state when a compiler fails to compile a piece of computer program source code, either due to errors in the code, or, more unusually, due to errors in the compiler itself. A compilation error message often helps programmers debugging the source code.
Different from most of the application bugs, compiler bugs are difficult to recognize as they usually manifest indirectly as application failures. For example, a compiler bug makes a program optimized and transformed into a wrong exe- cutable, and this bug can only manifest as the executable misbehaves.
There are 5 different types of errors in C programming language: Syntax error, Run Time error, Logical error, Semantic error, and Linker error. Syntax errors, linker errors, and semantic errors can be identified by the compiler during compilation.
Linker error This kind of errors are occurred, when the program is compiled successfully, and trying to link the different object file with the main object file. When this error is occurred, the executable is not generated, For example some wrong function prototyping, incorrect header file etc.
This is due to undefined behavior, you are accessing the array mc
out of bounds on the last iteration of your loop. Some compilers may perform aggressive loop optimization around the assumptions of no undefined behavior. The logic would be similar to the following:
mc
out of bounds is undefined behaviordi < 4
is always true since otherwise mc[di]
would invoke undefined behaviorgcc with optimization turned on and using the -fno-aggressive-loop-optimizations
flag causes the infinite loop behavior to disappear(see it live). While a live example with optimization but without -fno-aggressive-loop-optimizations exhibits the infinite loop behavior you observe.
A godbolt live example of the code shows the di < 4
check is removed and replaced with and unconditional jmp:
jmp .L6
This is almost identical to the case outlined in GCC pre-4.8 Breaks Broken SPEC 2006 Benchmarks. The comments to this article are excellent and well worth the read. It notes that clang caught the case in the article using -fsanitize=undefined
which I can not reproduce for this case but gcc using -fsanitize=undefined
does (see it live). Probably the most infamous bug around an optimizer making an inference around undefined behavior is the Linux kernel null pointer check removal.
Although this is an aggressive optimizations, it is important to note that as the C++ standard says undefined behavior is:
behavior for which this International Standard imposes no requirements
Which essentially means anything is possible and it notes (emphasis mine):
[...]Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).[...]
In order to get a warning from gcc we need to move the cout
outside the loop and then we see the following warning (see it live):
warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations] for(di=0; di<4;di++,delta=mc[di]){ } ^
which would have likely been sufficient to provide the OP with enough information to figure out what was going on. Inconsistency like this are typical of the types of behavior we can see with undefined behavior. To get a better understanding of why such waring can be inconsitent in the face of undefined behavior Why can't you warn when optimizing based on undefined behavior? is a good read.
Note, -fno-aggressive-loop-optimizations
is documented in the gcc 4.8 release notes.
Since you are incrementing di
before you use it to index mc
, the fourth time through the loop your will be referencing mc[4], which is past the end of your array, which could in turn lead to troublesome behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With