Segmentation fault в C++: что это за ошибка и как найти причину

Что такое segmentation fault (ошибка сегментации) в C++, почему программа падает и как найти причину: семь типичных ошибок с примерами, AddressSanitizer и отладчик.

Содержание

Segmentation fault, или ошибка сегментации, — аварийное завершение программы, которая обратилась к памяти, не принадлежащей ей. Операционная система замечает это и останавливает программу. Компилируется такой код без ошибок: проблема проявляется только при запуске.

Как выглядит ошибка

В Linux и macOS после падения в терминале появляется короткая строка:

TXT
Segmentation fault (core dumped)

Код завершения такой программы — 139. В Windows то же самое называется «нарушение прав доступа» (access violation) с кодом 0xC0000005. В CLion в конце вывода Вы увидите Process finished with exit code 139 (interrupted by signal 11: SIGSEGV).

Ни номера строки, ни имени переменной в сообщении нет. Поэтому главная трудность — не исправить ошибку, а найти её.

Почему компилятор не предупреждает

С точки зрения языка обращение к чужой памяти — неопределённое поведение. Стандарт не обещает, что программа упадёт: она может вывести мусор, испортить другую переменную или сработать «правильно» и сломаться через месяц на другом компьютере. Падение с segmentation fault — ещё удачный исход: ошибка хотя бы заметна.

Причина 1. Нулевой или неинициализированный указатель

C++
int* p = nullptr;
*p = 42;          // записи по нулевому адресу не существует

То же произойдёт, если указателю вообще не дали значения: в нём лежит случайный адрес.

C++
int* p;
*p = 42;          // адрес неизвестен

Решение: давайте указателю значение при объявлении и проверяйте его перед использованием.

C++
if (p != nullptr) {
    *p = 42;
}

Причина 2. Выход за границы массива

C++
int data[5] = {1, 2, 3, 4, 5};

for (int i = 0; i <= 5; ++i) {   // должно быть i < 5
    std::cout << data[i] << '\n';
}

В массиве из пяти элементов индексы идут от 0 до 4. Элемента data[5] нет. Чтение за границей часто проходит незаметно и даёт мусор, а запись портит соседние данные или роняет программу. Это самая частая причина ошибки сегментации у начинающих.

Решение: проверяйте условия циклов и пользуйтесь std::vector с методом at(). Он проверяет индекс и при ошибке бросает исключение std::out_of_range с понятным сообщением:

C++
std::vector<int> scores = {5, 4, 3};
std::cout << scores.at(10) << '\n';   // исключение вместо порчи памяти

Причина 3. Ссылка или указатель на уничтоженный объект

Локальная переменная исчезает, когда функция завершается. Ссылка на неё остаётся, но указывает в никуда.

C++
int& lastScore() {
    int score = 5;
    return score;      // score исчезнет при выходе из функции
}

Компилятор здесь предупреждает: reference to stack memory associated with local variable 'score' returned. Не пропускайте такие сообщения.

Та же ошибка с динамической памятью называется «использование после освобождения»:

C++
int* p = new int(5);
delete p;
std::cout << *p << '\n';   // памяти уже нет

Решение: возвращайте из функции значение, а не ссылку на локальную переменную. Вместо new и delete используйте std::vector, std::string и умные указатели — они освобождают память сами и вовремя.

Причина 4. Ссылки и итераторы после изменения вектора

C++
std::vector<int> numbers = {1, 2, 3};
int& first = numbers[0];

numbers.push_back(4);          // вектор мог переехать в другое место памяти
std::cout << first << '\n';    // ссылка указывает на старое место

Когда вектору не хватает места, он выделяет новый участок памяти и переносит туда элементы. Все прежние ссылки, указатели и итераторы на элементы после этого недействительны.

Решение: не храните ссылки на элементы вектора, пока добавляете или удаляете элементы. Храните индекс.

Причина 5. Бесконечная рекурсия

C++
int factorial(int n) {
    return n * factorial(n - 1);   // нет условия остановки
}

Каждый вызов функции занимает место в стеке. Рекурсия без условия остановки исчерпывает стек, и программа падает. Эту ситуацию называют переполнением стека (stack overflow).

Решение: у рекурсивной функции должна быть ветка, которая не вызывает её снова.

C++
int factorial(int n) {
    if (n <= 1) {
        return 1;
    }
    return n * factorial(n - 1);
}

Причина 6. Слишком большой массив на стеке

C++
int main() {
    int grid[10000000];   // около 40 мегабайт на стеке
    grid[0] = 1;
}

Размер стека ограничен — обычно несколькими мегабайтами. Большой локальный массив в него не помещается.

Решение: большие массивы храните в std::vector. Его элементы лежат в динамической памяти, где места намного больше.

C++
std::vector<int> grid(10000000);

Причина 7. Запись в строковый литерал

C++
char* text = (char*)"hello";
text[0] = 'H';          // строковые литералы изменять нельзя

Строковый литерал хранится в области памяти, доступной только для чтения.

Решение: для строк, которые нужно менять, используйте std::string.

Как найти место падения

AddressSanitizer

Это самый быстрый способ. Возьмём программу с ошибкой — в векторе три элемента, а читается четвёртый:

C++
#include <iostream>
#include <vector>

int main() {
    std::vector<int> scores = {5, 4, 3};
    int index = 3;
    std::cout << scores[index] << '\n';
}

Соберите её с двумя дополнительными флагами:

Bash
g++ -g -fsanitize=address,undefined main.cpp -o app
./app

Вместо молчаливой ошибки программа печатает подробный отчёт:

TXT
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000000dc
READ of size 4 at 0x6020000000dc thread T0
    #0 0x0001026f8b48 in main main.cpp:7
0x6020000000dc is located 0 bytes after 12-byte region

Читается он так: произошло чтение за границей участка памяти (heap-buffer-overflow), виновата седьмая строка файла main.cpp, обращение пришлось сразу за концом блока в 12 байт — то есть за концом вектора из трёх чисел int.

AddressSanitizer находит выход за границы, использование памяти после освобождения и обращение к уничтоженным локальным переменным. Работает в GCC и Clang на Linux и macOS; в Windows его поддерживает Visual Studio.

Отладчик

В CLion запустите программу кнопкой отладки (значок жука), а не обычным запуском. Когда программа упадёт, среда остановится на строке с ошибкой и покажет значения переменных и цепочку вызовов.

В терминале то же самое делает gdb:

Bash
g++ -g main.cpp -o app
gdb ./app

В gdb введите run, дождитесь падения и введите bt — отладчик покажет, какая функция и в какой строке выполнялась. В macOS вместо gdb используется lldb с теми же командами.

Предупреждения компилятора

Часть ошибок компилятор видит заранее. Собирайте учебные программы с флагами -Wall -Wextra и читайте всё, что он пишет: возврат ссылки на локальную переменную, использование неинициализированной переменной и бесконечная рекурсия попадают в предупреждения.

Как не допускать ошибку

  • Давайте переменным и указателям значение при объявлении.
  • Вместо обычных массивов используйте std::vector и std::string.
  • В циклах по массиву пишите i < size, а не i <= size.
  • Пока программа отлаживается, обращайтесь к элементам через at().
  • Не возвращайте ссылки и указатели на локальные переменные.
  • Не пишите new и delete без необходимости.
  • Собирайте учебные программы с флагами -Wall -Wextra -g -fsanitize=address,undefined.

Коротко

  • Segmentation fault — обращение к памяти, которой у программы нет.
  • Чаще всего виноваты выход за границы массива, нулевой указатель и ссылка на уничтоженный объект.
  • Сообщение об ошибке не называет строку; её находят AddressSanitizer и отладчик.
  • Контейнеры стандартной библиотеки и предупреждения компилятора предотвращают большую часть таких ошибок.

В стандарте C++26 появились проверки, которые ловят часть этих ошибок без дополнительных инструментов, — о них рассказано в статье Что нового в C++26. Если же программа не собирается вовсе, посмотрите разбор ошибки undefined reference.

Использованная литература

  1. Undefined behavior — cppreference.com [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/ub (дата обращения: 08.10.2026).
  2. AddressSanitizer — документация Clang [Электронный ресурс]. — URL: https://clang.llvm.org/docs/AddressSanitizer.html (дата обращения: 08.10.2026).
  3. ISO/IEC 14882:2024 — Язык программирования C++ [Электронный ресурс]. — URL: https://www.iso.org/standard/83626.html (дата обращения: 08.10.2026).