Search code examples
c++optimizationstack-dump

g++ optimization -O3 causing strange stackdump error?


First off, I only started programming C/C++ 2 months ago (but have more experience in Java) so I am far from experienced in C/C++. I'm working on a thesis and am using/expanding other code that has been written for previous research for that purpose.

Now, this error is definitely the strangest I have yet encountered and took me almost 3 hours to find and narrow down to the most elementary form i could reproduce it with.. Finally, I have these 2 files with the following code

c.hh:

#ifndef C_HH_
#define C_HH_

class complex {
public:
    double re;
};
#endif

test.cc:

#include <iostream>
#include "c.hh"

int main(int argc, char **argv) {
    complex* c;
    c->re = 0.0;

    for (int i = 0; i < 3; ++i) {
        c->re = (c->re) + (i==1)?0:1;
    }

    std::cout << c->re;
}

The only line I could take out and the error will still occur is the initialization of c.re, i.e. "c->re = 0.0;". However, I left this in because even if i remove the lower part of the code the error would still occur without this line, because c.re has not been initialized (or so i think?).

Everything else I found to be neccessary to reproduce the error, i.e.

1) the for loop. Will not cause an error if the lines for i=1,2,3 are written seperately.

2) the limits of i! e.g. will not cause an error if i only runs from 0 to 2. only occurs after at least 3 iterations.

3) the explicit assignment "c->re = (c->re) +", using "+=" instead of "= (c->re) +" will not cause the error.

4) the evaluation and(!) conditional check of i "(i==1)?0:1". Using if(..) to do this also causes the error, but it causes no error if i is not used, or no conditional check is performed.

5) the output of c.re. i.e. "std::cout << c->re;". Simply evaluating c.re ("c->re;") does not cause an error. It also does not cause an error if there is just an output without evaluating c.re at all. Doing the same via "fprintf(stdout, "%d", c->-re);" also causes the error.

Also very important is that the error only occurs if I compile the code like this:

g++ -O3 -c -o test.o test.cc
g++ -O3 -o test test.o c.hh

It will not cause an error if "-O3" is omitted, so I figure this is the real reason why the code produces this to me incomprehensible error. Note the -O3 is used because as I mentioned this code is written for research where functions like this might be called up to millions of times, so it is best to optimize as much as possible. I have however only adapted this "convention" from what has been given to me and don't really know the details and what exactly the result of using it, or not, are.

On top of that, whenever the error is caused, the program will not run at all. Meaning, even if I put any form of output anywhere, even at the very first line in the code, running "test" will immediately cause the error and not output anything.

Finally, the error i get upon trying to execute the program "test", is the following:

0 [main] test 10720 cygwin_exception::open_stackdumpfile: Dumping stack trace to test.exe.stackdump

Where the stackdump file contains the following:

Exception: STATUS_ACCESS_VIOLATION at eip=00401770
eax=00000001 ebx=0028CC8C ecx=8001801F edx=00000000 esi=0028CCA0 edi=0028CCA4
ebp=0028CC68 esp=0028CC50 program=D:\somepath\test\src\test.exe, pid 13768, thread main
cs=0023 ds=002B es=002B fs=0053 gs=002B ss=002B
Stack trace:
Frame     Function  Args
0028CC68  00401770 (00000001, 0028CC8C, 800280E8, 61007D35)
0028CD28  61007D9A (00000000, 0028CD84, 61006DC0, 00000000)
End of stack trace

As you might be able to tell I am very lost with this issue and have no idea what is going on, as there doesn't seem to be any programmatically "logic" reason as to why this happens. Is there anything I'm doing wrong in this very simple code? Is there anyway to "fix" this issue without having to leave out the -O3 optimization? Or is that maybe not that important?

I hope I provided enough information, thanks for any help!


Solution

  • complex* c;
    c->re = 0.0;
    

    This is very much undefined behaviour. The first line creates a pointer to an object of type complex but doesn't actually create the object itself. The pointer will point to an arbitrary location so that dereferencing it with -> is problematic.

    If you want to use a pointer, you need to actually create the object itself then point the pointer to it. This is as simple as replacing:

    complex *c;
    

    with:

    complex *c = new complex();
    

    and you should of course free it when you're done, with:

    delete c;
    

    However, there's no need for a dynamically allocated object here since the lifetime of it is strictly local. You could instead just substantiate an object with:

    object c;
    

    and then ensure you use it with . rather than ->, such as with:

    c.re = 0.0;
    

    In that case, no delete would be required.


    In addition, don't read too much into the bulk of your question regarding the possibly confusing situations in which it will work or not.

    Once you start doing undefined things, all bets are off and the program can pretty much do whatever it wants.