Menu
Coddy logo textTech

Что такое ошибка сегментации (segmentation fault)?

Ошибка сегментации (segmentation fault, segfault) это аварийное завершение программы, когда она пытается прочитать или записать память, к которой у неё нет доступа, например адрес 0 через нулевой указатель. Операционная система останавливает программу сигналом SIGSEGV.

Автор: Kevin Spektor, Сооснователь и CTO

Обновлено 24 сентября 2026 г.

Программа на C выводит первую строку, а потом останавливается с сообщением, которого никто не писал:

#include <stdio.h>

int main(void) {
    int *score = NULL;

    printf("About to read the score\n");
    printf("Score: %d\n", *score);
    return 0;
}
About to read the score
bash: line 1: 64030 Segmentation fault: 11  ./seg

Этот вывод получен в bash на macOS, где 64030 это идентификатор процесса, а 11 номер сигнала. В Linux то же падение обычно выглядит как Segmentation fault (core dumped). В любом случае программа так и не вывела оценку. В score хранится адрес 0, а *score просит процессор прочитать память по этому адресу, чего не разрешено делать ни одной программе.

Как возникает ошибка сегментации

Каждая программа работает в своём виртуальном адресном пространстве, огромном диапазоне адресов, который операционная система заполняет по частям. Она отображает в этот диапазон код программы, её глобальные переменные, стек и кучу блоками, которые называются страницами (4 КБ на большинстве систем Linux на x86, 16 КБ на Mac с процессорами Apple). Большинство адресов остаются неотображёнными, и адрес 0 всегда среди них, чтобы ошибки с нулевыми указателями ловились.

  1. Программа выполняет инструкцию, которая читает или записывает адрес. Здесь это чтение *score, адрес 0.
  2. Блок управления памятью процессора ищет адрес в таблице страниц. Страница не отображена, или программа пытается записать в страницу, доступную только для чтения.
  3. Процессор останавливает инструкцию и передаёт управление ядру через страничное прерывание (page fault).
  4. Ядро проверяет, могло ли обращение быть допустимым, например если стеку нужно вырасти. Оно недопустимо, поэтому ядро отправляет процессу сигнал 11, SIGSEGV.
  5. Действие по умолчанию для SIGSEGV это завершить процесс и, если система разрешает, сохранить дамп памяти (core dump). Затем оболочка выводит сообщение и устанавливает код выхода 139, то есть 128 плюс номер сигнала.

Значит, об ошибке сегментации не сообщает компилятор, и это не исключение, которое выбрасывает язык. Это аппаратура и ядро защищают память, так что это ошибка времени выполнения самого резкого вида. Windows обрабатывает то же событие как нарушение доступа, код исключения 0xC0000005.

Частые причины ошибок сегментации

C и C++ позволяют программе вычислить любой адрес и использовать его, поэтому все причины сводятся к использованию недопустимого адреса.

  • Разыменование нулевого указателя. int *p = NULL; *p = 5; Сюда же приводит функция, которая при неудаче возвращает NULL, например malloc или fopen, если её результат не проверен.
  • Индекс далеко за концом массива. C не проверяет границы, поэтому arr[1000000] это просто адрес на миллион элементов дальше.
  • Использование памяти после free. Указатель всё ещё хранит старый адрес, но память тебе больше не принадлежит.
  • Неинициализированный указатель. int *p; *p = 5; записывает по тому мусорному значению, которое случайно оказалось в p.
  • Переполнение стека. Рекурсивная функция без условия остановки добавляет кадры стека, пока не выйдет за его конец. Пример ниже упал на macOS с Segmentation fault: 11 и кодом выхода 139.
  • Запись в строковый литерал. char *name = "coddy"; name[0] = 'C'; пытается изменить память, доступную только для чтения. В Linux это segfault. На macOS та же программа остановилась с Bus error: 10, родственным сигналом.
#include <stdio.h>

int depth(int n) {
    return depth(n + 1) + 1;   /* never stops calling itself */
}

int main(void) {
    printf("%d\n", depth(0));
    return 0;
}

Небольшая ошибка часто вообще не приводит к падению, и от этого она опаснее. Этот цикл читает один элемент за концом массива из трёх элементов:

#include <stdio.h>

int main(void) {
    int scores[3] = {72, 88, 95};
    int total = 0;

    for (int i = 0; i <= 3; i++) {   /* <= reads scores[3] */
        total += scores[i];
    }
    printf("Total: %d\n", total);
    return 0;
}

Скомпилированная Clang на Mac, программа вывела Total: 256. Правильная сумма 255. scores[3] оказались следующие 4 байта стека, которые принадлежали программе, поэтому ошибки не было, и мусорное значение тихо прибавилось. Операционная система останавливает только обращения к памяти, которая программе вообще не принадлежит.

Исправление обеих ошибок одно и то же: знай, сколько элементов есть, и проверяй указатель, прежде чем по нему переходить.

Best: 95
No scores, nothing to read

Что означает «core dumped»

Дамп памяти (core dump) это файл с копией памяти программы в момент падения. Отладчик может открыть его позже и показать, где именно находилась программа и что хранилось в её переменных: gdb ./app core. Во многих дистрибутивах Linux эти файлы собирает systemd-coredump, и coredumpctl list их показывает. Когда дампы памяти отключены, например через ulimit -c 0, сообщение будет просто Segmentation fault, без слов в скобках.

Как найти строку, на которой программа упала

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

Отладчик. Скомпилируй с отладочной информацией и запусти программу в отладчике. Когда она остановится, bt (backtrace) выведет цепочку вызовов функций с именами файлов и номерами строк. В Linux отладчиком обычно служит gdb; на macOS это lldb, где bt работает так же.

gcc -g app.c -o app
gdb ./app
(gdb) run
(gdb) bt

AddressSanitizer. Скомпилируй с -fsanitize=address (его поддерживают и GCC, и Clang) и запусти программу как обычно. Вместо голого segfault он выведет отчёт, в котором назван вид ошибки, например heap-use-after-free или stack-buffer-overflow, со строкой, где произошло обращение, и строкой, где была выделена память. Он ловит и тихое чтение на один элемент за концом из примера выше, которое само по себе никогда не приводит к падению.

Valgrind. В Linux valgrind ./app запускает неизменённую программу и сообщает о каждом недопустимом чтении или записи, например Invalid read of size 4.

Ошибки сегментации в других языках

Python, Java и JavaScript проверяют каждый индекс и каждую ссылку перед использованием, поэтому те же ошибки превращаются в исключения с понятными сообщениями: IndexError или AttributeError в Python, ArrayIndexOutOfBoundsException или NullPointerException в Java, TypeError в JavaScript. Их можно перехватить с помощью обработки исключений. Segfault так обработать нельзя: это сигнал, и блок catch в C++ его не видит.

Программы на Python всё равно могут упасть с segfault, когда под ними ломается код на C. Эта строка просит ctypes прочитать адрес 0:

import ctypes
ctypes.string_at(0)

При запуске с python3 -X faulthandler Python 3.12 вывел Fatal Python error: Segmentation fault, а за этим строки Python, которые выполнялись. То же падение случается, когда в расширении на C или в нативной библиотеке есть ошибка работы с памятью. Rust идёт другим путём: его компилятор отвергает большую часть кода, который мог бы обратиться к недопустимой памяти, ещё до сборки программы.

Что читать дальше

Руководство по C об ошибках сегментации разбирает каждую причину на минимальной программе с исправлением. Чтобы не допускать этих ошибок с самого начала, прочитай про указатели, нулевые указатели и стек и кучу, а затем потренируйся в курсе C. О том, как сообщается об ошибках в языках, которые проверяют память за тебя, рассказано на странице про ошибку времени выполнения.

Часто задаваемые вопросы

Как исправить ошибку сегментации?
Сначала найди строку: скомпилируй с -g и запусти программу под gdb или lldb, а после падения набери bt, или скомпилируй с -fsanitize=address, чтобы получить подробный отчёт. Затем исправь указатель или индекс, который использует эта строка: проверяй указатели на NULL, держи индексы массивов меньше длины, не используй память после free и следи, чтобы рекурсия заканчивалась.
Ошибка сегментации это утечка памяти?
Нет, это противоположные проблемы. Утечка памяти это память, которую программа выделила и так и не освободила, поэтому программа продолжает работать и потребляет всё больше памяти. Ошибка сегментации это обращение к памяти, которая программе не принадлежит, и программа останавливается сразу. Ошибки с free, например использование указателя после освобождения, могут вызвать segfault, а забытый free приводит к утечкам.
Почему она называется ошибкой сегментации?
Название происходит от сегментации памяти, более старой схемы, в которой память программы делилась на сегменты с фиксированными границами, и обращение к адресу вне своего сегмента было ошибкой. Современные системы управляют памятью страницами, но название, как и имя сигнала SIGSEGV, осталось. В Windows то же событие называется нарушением доступа (access violation).
Может ли в Python возникнуть segmentation fault?
Обычный код на Python её не вызывает, потому что Python проверяет каждый индекс и каждую ссылку и вместо этого выбрасывает исключение. Но программа на Python всё равно может упасть с segfault внутри кода на C: в модуле расширения на C, в библиотеке вроде фреймворка машинного обучения или в ctypes. Запуск python -X faulthandler script.py выводит строки Python, которые выполнялись в момент падения.
Что означает код выхода 139?
Код выхода 139 означает, что процесс был убит сигналом 11, то есть SIGSEGV, ошибкой сегментации. Командные оболочки сообщают о смерти от сигнала как 128 плюс номер сигнала, а 128 + 11 = 139. В Docker и Kubernetes контейнер, завершившийся с кодом 139, получил segfault в своём основном процессе.
Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ