Поймал давеча два бага, связанные с "непредсказуемым" поведением в детерминированном коде. С одним возился несколько дней, т.к. место, в котором ошибка проявлялась, было совершенно не там, где ошибка находилась. Второй обнаружил быстро, но тоже неожиданно.
Тот, что попроще, выглядел примерно так, как описано
тут: при удалении массива программа "зависает" и, судя по отладчику, количество выделенной памяти не уменьшается. Увидев схожее поведение, я обнаружил и ошибку: действительно, при работе с массивом я обращаюсь к памяти за его пределами. То, что обращение к "не своей" памяти приводит к непредсказуемым эффектам в момент обращения, это я знаю. А вот то, что такое обращение может повлиять на процесс освобождения этой памяти гораздо позже этого обращения, оказалось неожиданным. Я так понимаю, как конкретно хранить размер массива - это
compiler-specific поведение, и в моём случае массив просто завершается неким стоп-символом, типа '\0'. Обращаясь к следующему за последним элементом массива я затираю этот стоп-символ, так что оператор delete[] либо пытается освободить вообще всю доступную память, либо просто начинает грустить.
Второй баг связан с тем, что я использовал метод std::sort вместе с дефолтным оператором сравнения для сортировки пары std::pair. Число double - не уникальный идентификатор, но случайное число в достаточно широком диапазоне. Каким-то образом так сложились звёзды, что два случайных числа оказалсь одинаковыми, а std::sort
не гарантирует сохранения порядка элементов в этом случае. Если бы сортировались просто числа, это не имело бы никакого значения, но я сортирую пары, которые содержат объект MyClass, каждый из которых уникальный. Из-за этого примерно в половине случаев после сортировки я получал список (..., object1, object2, ...), а в другой половине случаев - (..., object2, object1, ...), что создавало две альтернативные timelines. В нормальной ситуации это должно было бы обнаружиться сразу в момент расщепления timelines. Но меня интересует поведение не отдельных объектов, а их статистические свойства. В течение некоторого времени после расщепления timelines каждый из объектов продолжал жить своей жизнью, не оказывая никакого влияния на общую статистику, - до тех пока пока две timelines не разошлись достаточно далеко. Место и время, в которых эта разница проявилась, никак не указывали на настоящую локацию расщепления timelines, тем более на причину этого расщепления, так что пришлось повозиться. В итоге переключился на std::stable_sort и собственный оператор сравнения, что и решило исходную проблему.
This entry was originally posted at
https://glav.dreamwidth.org/678892.html. Please comment there using
OpenID.