g++ 优化 -O3 导致奇怪的堆栈转储错误?

g++ optimization -O3 causing strange stackdump error?

首先,我 2 个月前才开始编程 C/C++(但在 Java 方面有更多经验),所以我在 C/C++ 方面经验不足。我正在写一篇论文,并且是 using/expanding 为之前的研究编写的其他代码。

现在,这个错误绝对是我遇到过的最奇怪的错误,我花了将近 3 个小时才找到并缩小到我可以重现它的最基本形式。最后,我得到了以下 2 个文件代码

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;
}

我唯一可以删除但仍然会出现错误的行是 c.re 的初始化,即 "c->re = 0.0;"。但是,我把它留在里面是因为即使我删除了代码的下半部分,如果没有这一行,错误仍然会发生,因为 c.re 尚未初始化(或者我认为是这样?)。

我发现重现错误所必需的所有其他内容,即

1) for 循环。如果 i=1,2,3 的行分开写,不会导致错误。

2) i的极限!例如如果我只有从 0 到 2 的 运行s,将不会导致错误。仅在至少 3 次迭代后发生。

3) 显式赋值"c->re = (c->re) +",用“+=”代替“=(c->re)+”不会报错

4) i“(i==1)?0:1”的评估和(!)条件检查。使用 if(..) 这样做也会报错,但是如果没有使用 i ,或者不进行条件检查,则不会报错。

5) c.re的输出。即 "std::cout << c->re;"。简单地评估 c.re ("c->re;") 不会导致错误。如果只有输出而根本没有评估 c.re,它也不会导致错误。通过 "fprintf(stdout, "%d", c->-re); 做同样的事情也会导致错误。

同样重要的是,只有在我这样编译代码时才会出现错误:

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

省略“-O3”也不会报错,所以我想这才是代码产生这个令我难以理解的错误的真正原因。 请注意,使用 -O3 是因为正如我所提到的,此代码是为研究而编写的,其中此类函数可能会被调用多达数百万次,因此最好尽可能地进行优化。然而,我只是根据提供给我的内容改编了这个"convention",并不真正了解细节以及使用它或不使用它的确切结果是什么。

最重要的是,每当发生错误时,程序根本不会运行。意思是,即使我在任何地方放置 任何形式的输出 ,即使在代码的第一行,运行ning "test" 也会立即导致错误,而不是输出任何东西。

最后,我在尝试执行程序 "test" 时遇到的错误如下:

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

stackdump 文件包含以下内容:

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

正如您可能会说的那样,我对这个问题非常迷惑并且不知道发生了什么,因为似乎没有任何编程 "logic" 原因来解释为什么会发生这种情况。 在这个非常简单的代码中我做错了什么吗? 无论如何 "fix" 这个问题都不必省略 -O3 优化吗?或者这可能不是那么重要?

希望我提供了足够的信息,感谢您的帮助!

complex* c;
c->re = 0.0;

这是非常未定义的行为。第一行创建了一个指向 complex 类型对象的指针,但实际上并没有创建对象本身。该指针将指向任意位置,因此使用 -> 取消引用它是有问题的。

如果您想要使用指针,您需要实际创建对象本身然后将​​指针指向它。这就像替换一样简单:

complex *c;

与:

complex *c = new complex();

你当然应该在完成后释放它,使用:

delete c;

但是,这里 不需要 动态分配的对象,因为它的生命周期是严格本地的。您可以改为只用以下内容来证实一个对象:

object c;

然后确保将其与 . 一起使用,而不是 ->,例如:

c.re = 0.0;

在那种情况下,不需要 delete


此外,请不要 过多地阅读您的问题中有关它是否有效的可能令人困惑的情况。

一旦你开始做未定义的事情,所有的赌注都会消失,程序几乎可以做任何它想做的事。