Исправление ошибок сегментации в C ++

95

Я пишу кроссплатформенную программу на C ++ для Windows и Unix. На стороне окна код компилируется и выполняется без проблем. Со стороны Unix он компилируется, однако, когда я пытаюсь запустить его, я получаю ошибку сегментации. Моя первоначальная догадка заключается в том, что есть проблема с указателями.

Каковы хорошие методологии поиска и исправления ошибок ошибок сегментации?

Эльпезмуэрто
источник

Ответы:

135
  1. Скомпилируйте приложение с помощью -g, тогда в двоичном файле будут символы отладки.

  2. Используйте, gdbчтобы открыть консоль gdb.

  3. Используйте fileи передайте ему двоичный файл вашего приложения в консоли.

  4. Используйте runи передайте любые аргументы, необходимые вашему приложению для запуска.

  5. Сделайте что-нибудь, чтобы вызвать ошибку сегментации .

  6. Введите btв gdbконсоли, чтобы получить трассировку стека ошибки сегментации .

Svisstack
источник
Что значит компилировать gв контексте CMake?
Schütze
2
Включите тип сборки отладки. Один способ есть cmake -DCMAKE_BUILD_TYPE=Debug.
Антонин Десимо
36

Иногда сбой сам по себе не является реальной причиной проблемы - возможно, память была повреждена ранее, но потребовалось время, чтобы повреждение проявилось. Посмотрите valgrind , в котором есть множество проверок на наличие проблем с указателями (включая проверку границ массива). Он скажет вам, где начинается проблема , а не только строка, где происходит сбой.

палеозогт
источник
19

До того, как возникнет проблема, постарайтесь как можно больше ее избегать:

  • Компилируйте и запускайте свой код как можно чаще. Выявить неисправную деталь будет легче.
  • Попробуйте инкапсулировать низкоуровневые / подверженные ошибкам подпрограммы, чтобы вам редко приходилось работать напрямую с памятью (обратите внимание на моделирование вашей программы)
  • Поддерживайте набор тестов. Обзор того, что в настоящее время работает, а что больше не работает и т. Д., Поможет вам понять, в чем проблема ( возможно решение проблемы - тест Boost , я сам не использую его, но документация поможет понять, что именно информации должны отображаться).

Используйте соответствующие инструменты для отладки. В Unix:

  • GDB может сказать вам, где происходит сбой вашей программы, и позволит увидеть, в каком контексте.
  • Валгринд поможет вам обнаружить множество ошибок, связанных с памятью.
  • С GCC вы также можете использовать грязевик. С GCC, Clang и с октября экспериментально MSVC вы можете использовать Address / Memory Sanitizer . Он может обнаруживать некоторые ошибки, которых не делает Valgrind, и снижает производительность. Используется при компиляции с -fsanitize=addressфлагом.

Наконец, я бы порекомендовал обычные вещи. Чем больше ваша программа удобочитаема, удобна в обслуживании, понятна и аккуратна, тем проще ее будет отлаживать.

log0
источник
5

В Unix вы можете использовать valgrindдля поиска проблем. Это бесплатно и мощно. Если вы не хотите сделать это самостоятельно вы можете перегрузить newи deleteоператорам создать конфигурацию , в которой у вас есть 1 байт с 0xDEADBEEFдо и после каждого нового объекта. Затем отслеживайте, что происходит на каждой итерации. Это может не уловить все (вы не гарантируете, что даже прикоснетесь к этим байтам), но в прошлом у меня это работало на платформе Windows.

пшеница
источник
1
ну, это будет 4 байта, а не 1 ... но принцип в порядке.
Йонас Вагнер,
1
Могу ли я установить ссылку на мой ненавязчивый отладчик кучи ? :-)
fredoverflow
Действуй. Мы все здесь о помощи другим, поэтому нужно добавить все, что может помочь.
Wheaties
Хотя перегрузка newи deleteможет быть очень полезной, использование -fsanitize=address- лучший вариант, так как компилятор будет компилировать при обнаружении проблем во время выполнения и автоматически выгружает память на экран, что упрощает отладку.
Тарик Веллинг,
3

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

Чтобы избежать неинициализированных указателей в качестве локальных переменных, попробуйте объявить их как можно позже, желательно (а это не всегда возможно), когда они могут быть инициализированы значимым значением. Убедите себя в том, что они будут иметь ценность, прежде чем они будут использованы, изучив код. Если у вас возникли трудности с этим, инициализируйте их константой нулевого указателя (обычно обозначаемой как NULLили 0) и проверьте их.

Чтобы избежать неинициализированных указателей в качестве значений членов, убедитесь, что они правильно инициализированы в конструкторе и правильно обрабатываются в конструкторах копирования и операторах присваивания. Не полагайтесь на initфункцию для управления памятью, хотя вы можете для другой инициализации.

Если вашему классу не нужны конструкторы копирования или операторы присваивания, вы можете объявить их как частные функции-члены и никогда не определять их. Это вызовет ошибку компилятора, если они используются явно или неявно.

По возможности используйте умные указатели. Большим преимуществом здесь является то, что если вы будете придерживаться их и использовать их последовательно, вы можете полностью избежать записи, deleteи ничего не будет дважды удалено.

По возможности используйте строки и классы контейнеров C ++ вместо строк и массивов в стиле C. Рассмотрите возможность использования .at(i)вместо [i], потому что это приведет к принудительной проверке границ. Посмотрите, можно ли настроить ваш компилятор или библиотеку для проверки границ на[i] , по крайней мере, в режиме отладки. Ошибки сегментации могут быть вызваны переполнением буфера, которое приводит к записи мусора по совершенно исправным указателям.

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

Дэвид Торнли
источник
1

Я не знаю какой-либо методологии, чтобы исправить подобные вещи. Я не думаю, что можно было бы придумать что-то одно, потому что проблема заключается в том, что поведение вашей программы не определено (я не знаю ни одного случая, когда SEGFAULT не был вызван каким-то UB) .

Существуют всевозможные «методики», позволяющие избежать проблемы до того, как она возникнет. Один из важных - RAII.

Кроме того, вам просто нужно направить на это свои лучшие психические энергии.

Эдвард Стрэндж
источник