Наиболее распространенные причины нестабильных ошибок в C ++? - PullRequest
6 голосов
/ 28 августа 2009

В настоящее время я работаю над большим проектом и большую часть времени отлаживаю. Хотя отладка является нормальным процессом, существуют ошибки, которые являются нестабильными, и эти ошибки представляют наибольшую боль для разработчика. Программа не работает, ну, иногда ... Иногда она работает, и вы ничего не можете с этим поделать.

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

Позвольте мне начать список:

  1. с использованием неинициализированных переменных. Распечатки, похожие на mMember = mMember;
  2. синхронизация потоков. Иногда это может быть вопросом удачи;
  3. работа с не умными указатели, разыменование недействительным из них;

что еще?

Ответы [ 9 ]

22 голосов
/ 28 августа 2009

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

Я был частью команды для большого (несколько MLoC) приложения в течение нескольких лет, и количество сбоев в различных частях приложения хорошо коррелировало со стилем программирования, используемым в этих частях. На вопрос, почему они не меняют свой стиль программирования, некоторые из виновников ответили, что их стиль в целом дает большую производительность. (Мало того, что это неправильно, это также факт, что клиенты скорее имеют более стабильную, но более медленную программу, чем быструю, которая продолжает падать на них. Кроме того, большая часть их кода даже не требовала быть быстрой ...)

Что касается многопоточности: я не чувствую себя достаточно опытным, чтобы предлагать здесь решения, но я думаю, Столбцы Херба Саттера «Эффективный параллелизм» очень полезны для чтения по этому вопросу.

Изменить для обсуждения обсуждений в комментариях :

Я не писал, что "обработка строк в стиле C не является более производительной". (Конечно, в этом предложении много отрицания, но, поскольку я чувствую себя неправильно, я стараюсь быть точным.) Я сказал, что конструкции высокого уровня в целом не менее производительны: std::vector не в целом * На 1015 * медленнее, чем выполнение динамически размещаемых массивов C, поскольку это динамически размещаемый массив C. Конечно, есть случаи, когда что-то, закодированное в соответствии со специальными требованиями, будет работать лучше, чем любое общее решение - , но это не обязательно означает, что вам придется прибегнуть к ручному управлению памятью . Вот почему я написал, что если такие вещи необходимы, то только хорошо инкапсулированные в классах.

Но что еще более важно: в большинстве кодов разница не имеет значения. Нажатие кнопки на 0,01 с после нажатия на нее или 0,05 с просто не имеет значения, поэтому даже увеличение скорости в 5 раз не имеет значения для кода кнопки. Однако в случае сбоя кода значение всегда имеет значение.

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

5 голосов
/ 28 августа 2009

Условия гонки.

Это одна из немногих вещей, которая до сих пор вызывает дрожь в моем позвоночнике, когда возникает при отладке (или в системе отслеживания проблем). По сути, ужасно отлаживать и чрезвычайно легко создавать. Тремя наиболее распространенными причинами ошибок в моем программном обеспечении на C ++ были состояние гонки, зависимость от неинициализированной памяти и зависимость от статического порядка конструктора.

И если вы не знаете, каковы условия гонки, скорее всего, они являются причиной вашей нестабильности;)

5 голосов
/ 28 августа 2009

На самом деле я собирался опубликовать вопрос, который задавал прямо противоположное - находят ли другие, как я, что вы почти не тратите время на использование отладчика при работе с C ++? Я, честно говоря, не могу вспомнить, когда в последний раз использовал один - должно быть, это было около шести месяцев назад.

Честно говоря, если вы проводите большую часть времени в отладчике, я думаю, что с вашими базовыми методами кодирования что-то не так.

3 голосов
/ 28 августа 2009

Если вы действительно находитесь в ситуации, когда у вас уже есть плохой код, который ломается, лучший план, вероятно, состоит в том, чтобы использовать как можно больше инструментов (проверка памяти на уровне ОС / lib, автоматическое тестирование, ведение журнала, дамп ядра). и т. д.), чтобы найти проблемные зоны. Затем перепишите код, чтобы сделать что-то более детерминированное. Большая часть ошибок связана с людьми, которые в основном работают, но C ++ предлагает более надежные гарантии, если вы используете правильные инструменты и подходы.

2 голосов
/ 29 августа 2009

Еще не видел упомянутое:

  • Наследование от класса, в котором нет виртуального деструктора.
1 голос
/ 28 августа 2009

Не совсем проблема C ++, но замеченная в проекте C / C ++.

Самой сложной проблемой, с которой мне пришлось столкнуться, была проблема инициализации при запуске ОС на нашей платформе, которая приводила к необычным сбоям. Прошли годы, прежде чем мы узнали, что случилось. До этого мы запускали систему в одночасье, и если она не вылетала, то все было нормально. К счастью, ОС больше не продается.

1 голос
/ 28 августа 2009
  • переполнение буфера
  • с помощью указателей на удаленные объекты
  • возврат недействительных ссылок или ссылок на объекты вне области видимости
  • необработанные исключения
  • утечки ресурсов (не только памяти)
  • бесконечная рекурсия

  • несоответствие версий динамических библиотек

1 голос
/ 28 августа 2009
  • Чтение из некэшированной памяти, когда строка кэша записывается обратно в память (это правильный ублюдок для поиска).
  • Перезапись буфера
  • Переполнение стека!

Единственные 3, которые я могу вспомнить в настоящее время ... могут редактировать позже:)

0 голосов
/ 30 августа 2009

адресов и памяти, используемых до выделения или после освобождения, ошибки сегментации, массивы outofbounds, смещение, блокировки потоков, непонятная перегрузка операторов, встроенная сборка, пустой выход и вообще пустой, где требуются возвращаемые значения, усложняется, когда функции math.h стоит посмотреть поскольку все функции math.h имеют рабочие аргументы и возвращаемые значения по сравнению с другими библиотеками, имеющими слишком большое значение void, тесты на пустоту, nils, null и пустоты. 4 общих соглашения, которые я рекомендую - это возвращаемые значения, аргументы, троичные варианты выбора и обратимые изменения. Недостатком, которого следует избегать, являются векторы (вместо них используют массивы), имеющие пустые аргументы, и, по моему субъективному мнению, я избегаю оператора switch в пользу более понятного или читабельного, если ... elseif или более абстрактного "is".

C ++ также имеет довольно слабую прямую совместимость по сравнению со сценариями и интерпретацией, чтобы попробовать десятилетнюю Java, он все еще работает без изменений и безопасен в более поздней версии VM.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...