Включение g++-оптимизации вызывает segfault - я не понимаю

Я работал над своей программой, и я решил включить некоторые оптимизации с помощью g++ -O3. Внезапно моя программа начала segfault. Я искал проблемный код и минимизировал свою программу на что-то, что по-прежнему segfaults (только при использовании оптимизации уровня 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

struct vector
{
    float x, y, z;
    vector() : x(.0f), y(.0f), z(.0f) {}
    explicit vector(float f) : x(f), y(f), z(f) {}
    vector(float x, float y, float z) : x(x), y(y), z(z) {}
    // ...

Edit2: добавление вывода gdb при компиляции с помощью -g

(gdb) file a.out
Reading symbols from /home/rob/devel/gbug/a.out...done.
(gdb) run
Starting program: /home/rob/devel/gbug/a.out 

Program received signal SIGSEGV, Segmentation fault.
rt::light::Point::Point (this=0x804b008, pos=..., color=..., intensity=0.5)
    at 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)
    at src/rt/lights/point.cpp:13
#1  0x08048898 in main (argc=1, argv=0xbffff3e4) at src/main.cpp:5

Edit3: Источники для rt:: Цвет.

// include/rt/color.hpp
#ifndef RT_COLOR_HPP_
#define RT_COLOR_HPP_

#include "algebra/vector.hpp"

namespace rt {

/*******************************************************************************
 * CLASS DEFINITION
 */

struct Color
{
    float r, g, b;
    Color() : r(.0f), g(.0f), b(.0f) {}
    explicit Color(float f) : r(f), g(f), b(f) {}
    Color(float r, float g, float b) : r(r), g(g), b(b) {}

    Color& operator+= (const Color&);
    Color& operator*= (const Color&);
    Color& operator*= (float);
};

/*******************************************************************************
 * MEMBER OPERATORS
 */

inline Color& Color::operator+= (const Color& other)
{
    r += other.r;
    g += other.g;
    b += other.b;
    return *this;
}

inline Color& Color::operator*= (const Color& other)
{
    r *= other.r;
    g *= other.g;
    b *= other.b;
    return *this;
}

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

/*******************************************************************************
 * ADDITIONAL OPERATORS
 */

inline Color operator+ (Color lhs, const Color& rhs)
{
    return lhs += rhs;
}

inline Color operator* (Color lhs, const Color& rhs)
{
    return lhs *= rhs;
}

inline Color operator* (Color c, float f)
{
    return c *= f;
}

inline Color operator* (float f, Color c)
{
    return c *= f;
}

} // namespace rt

#endif

Ответ 1

При вычислении intensity * color косвенно этот оператор вызывается:

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

Он утверждает, что возвращает ссылку на Color, но этого не делает. Он должен вернуть ссылку на *this, как это делают другие операторы:

return *this;

Ответ 2

Вы должны понимать, что это не оптимизация, нарушающая ваш код, код уже нарушен. Я не могу понять, что происходит, просто глядя на эти куски, но, видя, что вы преувеличиваете новое утверждение, я попытался бы направить свои усилия на проверку входных параметров ваших новых функций. Среди многих вещей, которые происходят во время оптимизации 03, заключается в том, что компилятор попытается встроить вызовы функций, развернуть свои циклы и создать новые переменные, а также избавиться от них, чтобы ускорить выполнение. В качестве первого шага выполните двойную проверку везде, где есть петли, и убедитесь, что вы делаете что-то вроде я < strlen (str), что утверждение не сумасшедшее и утверждают, что входные параметры, которые не должны быть NULL, на самом деле не являются NULL.

Надеюсь, что это поможет.

Ответ 3

Время, чтобы узнать, как отлаживать с помощью gdb!

Скомпилируйте весь исходный код с помощью -g:

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

Затем запустите gdb, загрузите файл и выполните его:

gdb
file a.out
run

gdb приведет вас к командной строке, когда ваша программа попадет в segfault. В приглашении (gdb) введите "bt" и нажмите enter. Это даст вам трассировку стека. Первым фреймом стека будет строка кода, которая вызвала ошибку сегментации.

Оттуда, если это очевидно, исправить, иначе добавить вывод к вашему вопросу. Иногда gdb не очень хорош при отладке кода в конструкторах, но сначала попробуйте его и посмотрите, что он говорит.

Ответ 4

Я не уверен, что это проблема, которую вы видите, но если у вас есть класс с виртуальными функциями, который используется полиморфно, он должен иметь виртуальный деструктор:

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

В настоящее время, если вы бы указали delete переменную light в вашей основной, деструктор ~Light будет вызван (поскольку переменная имеет тип Light*), вместо правильного ~Point. Создание виртуального destructor устраняет это.

Ответ 5

Может быть некоторая путаница с использованием переменной-члена pos и переданной в переменной pos, попробуйте называть их разными именами переменных.

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