Почему размер стека вызова (call stack) ограничен?

«Почему размер стека вызова (call stack) ограничен?» — вопрос из категории Управление памятью, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Стек вызова (call stack) имеет ограниченный размер по нескольким фундаментальным причинам, связанным с управлением памятью, производительностью и стабильностью программы.

Основные причины:

  1. Предопределённое выделение: Стек для потока выделяется операционной системой при его создании. Его размер фиксирован (обычно от 1 до 8 МБ в настольных ОС) и задаётся либо по умолчанию компоновщиком/ОС, либо явно через атрибуты потока.
  2. Производительность и простота: Работа со стеком сводится к перемещению одного указателя (регистр SP или RSP на x86-64). Выделение (push) и освобождение (pop) памяти — операции O(1). Фиксированный, непрерывный регион памяти обеспечивает отличную локальность данных и кэшируемость.
  3. Защита от сбоев: Ограничение — это защитный механизм. Без него ошибка, такая как бесконечная рекурсия или выделение гигантского локального массива, привела бы к тому, что процесс бесконтрольно поглощал бы всю доступную память, вызывая крах всей системы или серьезную фрагментацию.
  4. Поддержка множества потоков: В многопоточном приложении у каждого потока свой стек. Ограничение размера позволяет создавать сотни потоков, не исчерпывая виртуальную память процесса.

Пример переполнения стека в C++:

void causeStackOverflow() {
    int largeArray[1000000]; // Выделение ~4 МБ в стеке (может превысить лимит)
    // ... использование массива
}

void infiniteRecursion() { // Классический пример
    infiniteRecursion(); // Каждый вызов кладёт в стек новый фрейм
}

Что делать при необходимости больших данных? Использовать динамическую память (кучу) через std::vector, std::unique_ptr или прямой new/delete.