Таинственный гейзенбаг? - PullRequest
5 голосов
/ 13 сентября 2010

Итак, я делаю игру со змеями с телепортами и обычными мышами.У меня был такой цикл:

while(snake.alive() && miceEaten < micePerLevel)
{
    displayInfo(lives, score, level, micePerLevel - miceEaten);
    //some code
    if(miceEaten())
    {
        //update score...
    }
    //more stuff...
}

Проблема с приведенным выше кодом состояла в том, что displayInfo вызывается до обновления счета, и поэтому после употребления мыши пользователь должен ждать, покацикл запускается снова, чтобы увидеть его счет обновляется.Поэтому я переместил одну строку кода в конец функции:

while(snake.alive() && miceEaten < micePerLevel)
{
    //some code
    if(miceEaten())
    {
        //update score...
    }
    //more stuff...
    displayInfo(lives, score, level, micePerLevel - miceEaten);
}

, и телепорты перестали работать!Программа вылетает всякий раз, когда змея достигает телепорта.И displayInfo использует следующий код:

stringstream s;
s << "LEVEL " << left << setw(12) << level << "LIVES: " << setw(12) << lives << "MICE LEFT: " << setw(12) << miceLeft
    << "SCORE: " << setw(13) << score;
printLine(0, s.str(), WHITEONBLUE);

, где printLine имеет только color_set, mvprintw и refresh().Ничего общего с телепортами.Странно.

Итак, я пошел к функции змеи, где змея получает свое следующее местоположение из телепорта:

    body.push_back(teleports[overlap(next)]->teleportFrom(dir)); //next is a Location object

Где teleports[overlap(next)]->teleportFrom(dir) возвращает местоположение, в которое змея должна быть телепортирована.В попытке понять, почему происходит сбой (возможно, Teleport возвращал какое-то местоположение за кадром?), Я добавил следующие 3 строки перед строкой выше:

    Location l = teleports[overlap(next)]->teleportFrom(dir);
    mvprintw(1, 0, "(%i, %i)", l.x, l.y);
    refresh();

И проблема исчезла!

Не только это, но я должен иметь эти три строки.Если я закомментирую mvprintw(1, 0, "(%i, %i)", l.x, l.y);, или refresh();, или и то и другое, программа, как и прежде, аварийно завершает работу при достижении телепорта.

Любые идеи о том, что может вызывать такое поведение?* ОБНОВЛЕНИЕ: Я попытался удалить все предупреждения (которые были в основном предупреждениями о сравнении чисел со знаком / без знака), но пока остается только 1:

warning: reference to local variable 'other' returned

И код:

Location& Location::operator = (Location other)
{
    if(this == &other)
        return other;
    x = other.x;
    y = other.y;
    return *this;
}

Что мне сделать, чтобы исправить это предупреждение?

Ответы [ 4 ]

7 голосов
/ 13 сентября 2010

Создайте свой оператор присваивания следующим образом:
Вы всегда должны возвращать * this (даже если они были равны).Но они никогда этого не сделали, так как вы создавали локальную копию (так что это не ваша ошибка).

Location& Location::operator = (Location const& other)
{
    // Does it really matter if you assign to self?
    x = other.x;
    y = other.y;
    return *this;
}

Стандартная копия и своп показались немного излишними для такого простого класса.

PS.Вы должны исправить все предупреждения (даже если они так же просты, как несоответствие без знака).Если вы не исправите их, вы станете невосприимчивыми к их потенции и не обнаружите реальной проблемы, потому что она окружена предупреждением о том, что вы игнорируете.Поэтому исправьте их все (я всегда включаю флаг, который заставляет компилятор обрабатывать все предупреждения как ошибки, чтобы код не компилировался при наличии каких-либо предупреждений).

Правильный способ реализации оператора присваивания (илинаиболее распространенный хороший способ).Является ли использовать копию и обменять идиому:

// notice the parameter is passed by value (i.e. a copy).
// So the copy part is aromatically taken care of here.
// So now you just need tom implement the swap() part of the idiom.
Location& Location::operator = (Location other)
{
    this->swap(other);
    return *this;
}

void Location::swap(Location& other)
{
    std::swap(x, other.x);
    std::swap(y, other.y);
}
4 голосов
/ 13 сентября 2010
Location& Location::operator = (Location other)
{
    if(this == &other)
        return other;
    x = other.x;
    y = other.y;
    return *this;
}

Возвращает ссылку. Когда функция возвращается, что происходит с other? (Он умирает, и вы ни на что не ссылаетесь.) Поскольку это класс, с которым вы работаете в проблемной области, возможно, это и есть причина. Переупорядочение окружающего кода оставляет стек в определенном состоянии, когда ссылка на мертвую переменную «работает».

Измените его на return *this или просто полностью удалите чек. (Назначение двух переменных без ветви, вероятно, всегда будет выполняться быстрее, чем добавление ветви на современном CPU.)

(Обычно вы также должны принимать параметр по ссылке, а не по значению.)

3 голосов
/ 13 сентября 2010

Вы проверили свой код, который вызывает эту аномалию? Гейзенбаг феномен, приведенный здесь:

Одним из распространенных примеров является ошибка, которая возникает в программе, которая была скомпилирована с оптимизирующим компилятором, но не в той же самой программе, когда компилируется без оптимизации (например, для генерации версии в режиме отладки)

Вот несколько рекомендаций:

  • Состояние гонки? вы используете темы?
  • Граница переполнения указателя где-нибудь?
  • Запустите ваш код через valgrind , чтобы отслеживать любые необычные / ошибочные изменения в буферах памяти где-то

Другая цитата:

Одной из распространенных причин поведения, похожего на heisenbug, является то, что выполнение программы в режиме отладки часто очищает память перед запуском программы и помещает переменные в ячейки стека, а не хранит их в регистрах. Эти различия в исполнении могут изменить эффект ошибок, связанных с доступом к элементу за пределами допустимого или неправильными предположениями о начальном содержании памяти. Другая причина заключается в том, что отладчики обычно предоставляют функции наблюдения или другие пользовательские интерфейсы, которые вызывают выполнение дополнительного кода (такого как средства доступа к свойствам), что, в свою очередь, может изменить состояние программы. Еще одна причина - фанданго на ядре, эффект указателя, выходящего за пределы. В C ++ многие heisenbugs вызваны неинициализированными переменными.

Убедитесь, что коммутаторы выключены - без оптимизации, полной отладочной информации, очистки всех существующих сборок, перезапустите IDE и повторите компиляцию ....

1 голос
/ 13 сентября 2010

Прежде всего, ваш Location :: operator = должен быть таким:

Location& Location::operator = (const Location &other)
{
    if(this == &other)
        return *this;
    x = other.x;
    y = other.y;
    return *this;
}

Однако это, вероятно, не объясняет сбой. Плохие указатели на стек здесь не рушатся на большинстве архитектур (при условии, что x и y - int).

Так вот, это Мандельбаг, а не Гейзенбаг. У вас есть кто-то еще где-то портит память. Удачи.

...