Segmentation fault: почему программа падает
Segmentation fault (ошибка сегментации) — программа обратилась к памяти, которая ей не принадлежит, и система её остановила. Это ошибка при работе, а не при сборке: компилятор её не видит.
Как она выглядит:
- Linux и macOS:
Segmentation fault (core dumped), код завершения 139 (core dumpedзначит, что система сохранила снимок памяти программы для разбора); - Windows: программа просто закрывается, а Code::Blocks пишет
Process returned -1073741819 (0xC0000005); при переполнении стека —-1073741571 (0xC00000FD); - на олимпиадах в проверяющих системах — «ошибка выполнения» (Runtime Error, RE).
Частые причины
1. Выход за границы массива или вектора. Индекс n у массива из n элементов, индекс −1, v[0] у пустого вектора, v.back() у пустого вектора. Проверьте границы всех циклов: i < n, а не i <= n.
2. Большой массив внутри функции. Локальные переменные лежат в стеке, а он небольшой: обычно 8 МБ в Linux и 1 МБ в программах Visual Studio. Массив int a[10000000] (40 МБ) внутри main его переполняет. Решение — объявить массив вне функций (глобальные переменные лежат не в стеке) или взять vector:
#include <iostream>
#include <vector>
using namespace std;
const int N = 10000000;
int a[N]; // глобальный: не в стеке, сразу заполнен нулями
int main() {
vector<int> b(N); // vector хранит элементы в куче
a[N - 1] = 1;
b[N - 1] = 2;
cout << a[N - 1] + b[N - 1] << "\n";
return 0;
}33. Бесконечная или слишком глубокая рекурсия. Нет базы рекурсии или она никогда не достигается — каждый вызов занимает место в стеке, и он кончается. Глубина в сотни тысяч вызовов тоже может переполнить стек даже у правильной функции.
4. Пустой или испорченный указатель. Разыменование nullptr, указателя без начального значения или указателя на уже освобождённую память (после delete).
5. Деление на ноль целых чисел в Linux тоже роняет программу, но с другим сообщением — Floating point exception.
Как найти место ошибки
Санитайзер адресов. В Linux и macOS соберите программу так:
g++ -std=c++17 -g -fsanitize=address,undefined -o prog prog.cpp
./progПри выходе за границы программа остановится и напишет, в какой строке файла это случилось. Ключ -g добавляет в программу номера строк. В MinGW для Windows санитайзер обычно не работает; в Visual Studio есть свой — ключ /fsanitize=address.
Проверка индексов вектора. Ключ -D_GLIBCXX_DEBUG (только для g++) заставляет v[i] проверять индекс: при ошибке программа остановится с понятным сообщением. Тот же эффект в коде даёт v.at(i) вместо v[i].
Отладчик gdb (программа, которая запускает вашу и показывает, где она остановилась). Соберите с -g, запустите gdb ./prog, наберите run, а после падения — bt: он покажет цепочку вызовов до строки, где произошла ошибка.
Вывод по шагам. Самый простой способ: выводите промежуточные значения в cerr — поток для сообщений об ошибках, он не задерживает вывод (cerr << i << "\n";), — и смотрите, после какого вывода программа упала.
Выход за границы не всегда роняет программу сразу. Иногда она тихо портит соседние переменные и выдаёт неверный ответ. Если ответ странный, а логика верна, проверьте индексы.