Segmentation fault: почему программа падает
Segmentation fault (ошибка сегментации) — программа обратилась к памяти, которая ей не принадлежит, и система её остановила. Это ошибка при работе, а не при сборке: компилятор её обычно не видит.
Как она выглядит:
- Linux и macOS:
Segmentation fault (core dumped)или простоSegmentation fault, код завершения 139 (core dumpedзначит, что система сохранила снимок памяти программы для разбора); - Windows: программа молча закрывается, а Code::Blocks пишет
Process returned -1073741819 (0xC0000005); при переполнении стека —-1073741571 (0xC00000FD); - проверяющие системы олимпиад: «ошибка выполнения» (Runtime Error, RE).
Частые причины
1. Выход за границы массива. Индекс n у массива из n элементов, отрицательный индекс, i <= n вместо i < n в цикле. C индексы не проверяет. Небольшой выход часто не роняет программу, а молча портит соседние переменные; большой — роняет.
2. scanf без &. scanf("%d", n) вместо scanf("%d", &n): число записывается по «адресу», равному мусору в n. С ключом -Wall gcc предупреждает: format '%d' expects argument of type 'int *', but argument 2 has type 'int'.
3. Не проверен результат fopen или malloc. Файл не нашёлся — fopen вернул NULL, и первое же fscanf падает. Особенно часто это бывает, когда файл лежит не в той папке, из которой запускается программа. Проверяйте:
#include <stdio.h>
int main(void) {
FILE *f = fopen("input.txt", "r");
if (f == NULL) {
perror("input.txt");
return 1;
}
int x;
if (fscanf(f, "%d", &x) == 1) {
printf("%d\n", x);
}
fclose(f);
return 0;
}Если файла нет, программа напишет input.txt: No such file or directory и завершится, а не упадёт.
4. Изменение строки-литерала.
#include <stdio.h>
int main(void) {
char *s = "hello";
s[0] = 'H';
printf("%s\n", s);
return 0;
}Результат не определён: строка в кавычках обычно лежит в памяти только для чтения, и в Linux программа падает на s[0] = 'H'. Чтобы строку можно было менять, объявите массив: char s[] = "hello";.
5. Указатель без значения или после free. int *p; *p = 1; — запись по случайному адресу. После free(p) память больше не принадлежит программе, обращение к ней — ошибка, даже если иногда «работает».
6. Большой массив внутри функции. Локальные переменные лежат в стеке: обычно 8 МБ в Linux и 1 МБ в Windows. int a[5000000] (20 МБ) внутри main его переполняет — программа падает сразу при запуске. Объявите массив вне функций (глобальные переменные лежат не в стеке) или выделите malloc.
7. Слишком глубокая рекурсия. Нет базы рекурсии или она не достигается — стек кончается.
Как найти место ошибки
Санитайзер адресов. В Linux и macOS соберите программу так:
gcc -g -fsanitize=address,undefined prog.c -o prog
./progПри выходе за границы массива, обращении к освобождённой памяти и многих других ошибках программа остановится и напишет, что случилось и в какой строке. Ключ -g добавляет в программу номера строк. В MinGW для Windows санитайзер обычно не работает; в Visual Studio есть свой — ключ /fsanitize=address.
Отладчик gdb (программа, которая запускает вашу и показывает, где она остановилась). Соберите с -g, запустите gdb ./prog, наберите run, а после падения — bt: появится цепочка вызовов с номером строки, где программа упала. Выход из gdb — quit.
valgrind (Linux): valgrind ./prog показывает чтение и запись за пределами выделенной памяти и утечки. Ставится командой sudo apt install valgrind.
Вывод по шагам. Самый простой способ: выводите промежуточные значения в stderr (поток для сообщений об ошибках) — fprintf(stderr, "i = %d\n", i); — и смотрите, после какого вывода программа упала. В отличие от printf, вывод в stderr не задерживается в буфере и не пропадает при падении.
Если программа падает не каждый раз или по-разному на разных компьютерах, это почти всегда выход за границы массива или переменная без начального значения. Санитайзер находит такие ошибки быстрее всего.