【问题标题】:Turning on g++ optimization causes segfault - I don't get it打开 g++ 优化会导致段错误 - 我不明白
【发布时间】:2011-06-15 22:11:35
【问题描述】:

我一直在研究我的程序,我决定使用g++ -O3 开启一些优化。突然,我的程序开始出现段错误。我已经找到了有问题的代码,并将我的程序最小化为仍然存在段错误的东西(仅在使用 3 级优化时)。我希望有人可以快速浏览一下代码(我尽量将其最小化):

// src/main.cpp
#include "rt/lights/point.hpp"
int main(int argc, char **argv)
{
    rt::Light *light = new rt::light::Point(alg::vector(.0f, 5.0f, 5.0f), rt::Color(1.0f), .5f);
    return 0;
}


// include/rt/lights/point.hpp
#ifndef RT_LIGHT_POINT_HPP_
#define RT_LIGHT_POINT_HPP_

#include "rt/accelerator.hpp"
#include "rt/color.hpp"
#include "rt/intersection.hpp"
#include "rt/light.hpp" // abstract

namespace rt {
namespace light {

class Point : public Light
{
  public:
    Point(alg::vector pos, Color color, float intensity) : Light(intensity * color), pos(pos) {}

    Color get_contrib(const Intersection&, const Accelerator&, const alg::vector& toViewer) const;

  private:
    alg::vector pos;
};

} // namespace light
} // namespace rt

#endif


// include/rt/light.hpp
#ifndef RT_LIGHT_HPP_
#define RT_LIGHT_HPP_

#include "algebra/vector.hpp"
#include "rt/color.hpp"

namespace rt {

class Intersection;
class Accelerator;

class Light
{
  public:
    Light(Color intensity) : intensity(intensity) {}

    virtual Color get_contrib(const Intersection&, const Accelerator&, const alg::vector& toViewer) const = 0;

    Color get_intensity() const {return intensity;}

  protected:
    Color intensity;
};

} // namespace rt

#endif

我想了解一下为什么这段代码只会在使用优化时出现段错误,以及如何阻止它这样做。谢谢!

$ find src/ -name "*.cpp" | xargs g++ -I include/ -O3
$ ./a.out
Segmentation fault

编辑:根据要求,alg::vector 的构造函数

结构向量 { 浮动 x, y, z; 矢量():x(.0f),y(.0f),z(.0f){} 显式向量(float f):x(f),y(f),z(f){} 矢量(浮动 x,浮动 y,浮动 z):x(x),y​​(y),z(z){} // ...

Edit2:使用 -g 编译时添加 gdb 输出

(gdb) 文件 a.out 从 /home/rob/devel/gbug/a.out...读取符号...完成。 (gdb) 运行 启动程序:/home/rob/devel/gbug/a.out 程序收到信号 SIGSEGV,分段错误。 rt::light::Point::Point (this=0x804b008, pos=..., color=..., intensity=0.5) 在 src/rt/lights/point.cpp:13 13 Point::Point(alg::vector pos, Color color, float intensity) : Light(intensity * color), pos(pos) (gdb) BT #0 rt::light::Point::Point (this=0x804b008, pos=..., color=..., intensity=0.5) 在 src/rt/lights/point.cpp:13 #1 0x08048898 in main (argc=1, argv=0xbffff3e4) at src/main.cpp:5

Edit3:rt::Color 的来源。

// 包含/rt/color.hpp #ifndef RT_COLOR_HPP_ #define RT_COLOR_HPP_ #include "代数/向量.hpp" 命名空间 rt { /************************************************* ****************************** * 类定义 */ 结构颜色 { 浮动r,g,b; 颜色():r(.0f),g(.0f),b(.0f){} 显式颜色(float f):r(f),g(f),b(f){} 颜色(float r, float g, float b) : r(r), g(g), b(b) {} 颜色& 运算符+= (const 颜色&); 颜色&运算符*= (const Color&); 颜色& 运算符*= (float); }; /************************************************* ****************************** * 会员运营商 */ 内联颜色和颜色::operator+= (const Color& other) { r += 其他.r; g += 其他.g; b += 其他.b; 返回*这个; } 内联颜色和颜色::operator*= (const Color& other) { r *= 其他.r; g *= 其他.g; b *= 其他.b; 返回*这个; } 内联颜色和颜色::operator*= (float f) { r *= f; g *= f; b *= f; } /************************************************* ****************************** * 其他运算符 */ 内联颜色运算符+(颜色 lhs,const Color& rhs) { 返回 lhs += rhs; } 内联颜色运算符*(颜色 lhs、const Color& rhs) { 返回 lhs *= rhs; } 内联颜色运算符*(颜色 c,浮点 f) { 返回 c *= f; } 内联颜色运算符* (float f, Color c) { 返回 c *= f; } } // 命名空间 rt #万一

【问题讨论】:

  • 您是否尝试过使用 -g 进行编译以查看回溯是否完全可用?
  • 你能把alg::vector的拷贝构造函数贴出来吗?
  • operator*() 看起来如何用于将Color 与强度相乘?
  • 我也会用-Wall -Wextra 编译代码。如果您收到警告,它们可能包含问题的线索。
  • @chrisaycock,GCC 可以将多个源编译成一个 a.out,所以这里不是问题。也请求颜色的来源。

标签: c++ optimization g++ segmentation-fault


【解决方案1】:

在计算intensity * color时,间接调用了这个算子:

inline Color& Color::operator*= (float f)
{
    r *= f;
    g *= f;
    b *= f;
}

它声称返回对Color 的引用,但没有。它应该返回对*this 的引用,就像其他运算符一样:

return *this;

【讨论】:

  • +1。这怎么不是编译器错误?缺少返回值是 gcc 中的警告吗?
  • @James:如果您指定-Wall,则会启用警告。
  • 啊。我想这里有一个教训......总是使用 -Wall。
  • @James: 并使用-Werror,这样您就不会变得懒惰并把警告留给“以后”。
  • @Yakk-AdamNevraumont:将所有警告追溯添加到现有项目可能很困难(但总是值得的)。但是,您从头开始的任何项目都会使所有警告都成为错误,并且可以防止很多问题。注意:虽然您可能认为-Wextra 是可选的,但如果您仔细阅读它们,它们通常是您的想法中的logical 错误,实际上指出了需要修复的实际问题。如果你不这样做,那么一个极端案例将在未来的某个时候炸毁你的代码。负责在所有新项目中打开所有警告将所有警告视为错误。
【解决方案2】:

是时候学习如何使用 gdb 进行调试了!

用-g重新编译所有源代码:

find src/ -name "*.cpp" | xargs g++ -I include/ -O3 -g

然后运行 ​​gdb,加载您的文件并执行它:

gdb
file a.out
run

当您的程序遇到段错误时,gdb 将带您进入命令提示符。在(gdb)提示符下输入“bt”并回车。它会给你堆栈跟踪。堆栈的第一帧将是导致分段错误的代码行。

如果很明显,请从那里修复,否则将输出添加到您的问题中。有时 gdb 并不擅长调试构造函数中的代码,但请先尝试一下,看看它说了什么。

【讨论】:

  • 我将 gdb 输出添加到原始帖子中。感谢您给我调试的快速指南!我不太确定这个输出对我有什么帮助,因为我已经知道段错误发生在哪里。我只是不知道为什么......
  • 除非分段错误可能在错误代码之后发生很久。 (未定义的行为是未定义的。)
【解决方案3】:

您必须意识到,破坏代码的不是优化,而是代码已经被破坏。我不能仅仅通过查看这些块来完全了解发生了什么,但是如果你看到你正在对一个新语句进行分段错误,我会尝试将我的努力转向检查你的新函数的输入参数。在 03 优化期间发生的许多事情中,编译器将尝试内联函数调用、展开循环、创建新变量以及删除变量以加速执行。第一步,仔细检查你有循环的任何地方,并确保你是否正在做类似 i

希望这会有所帮助。

【讨论】:

    【解决方案4】:

    我不确定这是否是您看到的问题,但如果您有一个具有多态使用的虚函数的类,它应该有一个虚析构函数:

    class Light {
       ...
       virtual ~Light {}
       ...
    };
    

    目前,如果您在 main 中使用 delete light 变量,则将调用 ~Light 析构函数(因为该变量的类型为 Light*),而不是正确的 ~Point 一个。将析构函数设为虚拟可以解决此问题。

    【讨论】:

    • 我添加了虚拟析构函数,但无济于事...... segfault发生在对象再次被销毁之前,它发生在new rt::light::Point的行上。任何过去甚至不再执行的事情。
    • @robrene:我也有同样的怀疑,因为在示例代码中对象 is 从未真正被销毁......但无论如何它应该是朝着正确方向迈出的一步。
    【解决方案5】:

    使用成员变量 pos 和传入的变量 pos 可能会有些混淆,请尝试将它们命名为不同的变量名。

    Point(alg::vector pos, Color color, float intensity) : Light(intensity * color), pos(pos) {}

    【讨论】:

    • 不应该有任何混淆。该参数隐藏了成员变量。在初始化列表中包含 pos(pos) 之类的内容很常见。
    猜你喜欢
    • 1970-01-01
    • 2023-04-10
    • 2016-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-02
    相关资源
    最近更新 更多