Стек переполнения (stack overflow) — это ситуация, когда программа затрачивает больше памяти стека, чем выделено системой. Исторически стек использовался для хранения локальных переменных, адресов возврата и временных данных при вызовах функций. В ранних реализациях C стек был довольно мал и не защищён от выхода за пределы.
Проблема возникает, когда функция или цепочка вызовов функций задействует слишком много локальных переменных или вызывает рекурсивные функции без условия выхода, из-за чего программа записывает данные за пределами выделенного памяти стека, приводя к ошибкам, сбоям и уязвимостям.
Решение — проектировать скромные по памяти функции, избегать глубоких или бесконечных рекурсий, не размещать крупные объекты на стеке. Операционная система может предотвращать переполнение с помощью защиты сегментов памяти (guard pages), однако разработчик обязан писать код, не приводящий к переполнению.
Пример кода, вызывающего stack overflow из-за бесконечной рекурсии:
void foo() { int arr[1000]; // Крупный локальный массив только ускоряет проблему foo(); // Рекурсивный вызов без выхода } int main() { foo(); return 0; }
Ключевые особенности:
Что произойдет, если объявить очень большой локальный массив внутри функции (например, int arr[1000000])?
Ответ: Большой локальный массив может сразу использовать весь стек. В зависимости от ОС и компилятора это приведёт к сбою при запуске функции или даже к краху программы.
Пример кода:
void func() { int arr[1000000]; // Очень много памяти arr[0] = 1; }
Рекурсия всегда приводит к переполнению стека?
Ответ: Нет, рекурсия полезна, если ограничена по глубине. Переполнение возникает только если глубина рекурсии велика или она не ограничена.
Можно ли размещать крупные статические массивы внутри функций для экономии памяти?
Ответ: Нет, большие static массивы внутри функции всё равно занимают память, но уже в сегменте статических данных, а не стека. Это не всегда экономично, особенно если нужна временная локальная память.
Пример кода:
void func() { static int arr[1000000]; // Не на стеке, но статическая область занята навсегда }
Программист реализовал быструю сортировку через рекурсию для сортировки большого массива, не ограничив глубину вызовов и не использовал условия выхода. Код приводил к stack overflow при обработке реальных данных.
Плюсы:
Минусы:
Другой программист использовал итеративную реализацию с собственным небольшим стеком на куче, контролировал глубину рекурсии и выделял крупные временные массивы через malloc.
Плюсы:
Минусы: