В программном обеспечении переполнение стека (англ. stack overflow) возникает, когда в стеке вызовов хранится больше информации, чем он может вместить. Обычно ёмкость стека задаётся при старте программы/потока. Когда указатель стека выходит за границы, программа аварийно завершает работу.[1]
Эта ошибка случается по трём причинам.[2]
Простейший пример бесконечной рекурсии на Си:
int foo() {
return foo();
}
Функция будет вызывать сама себя, расходуя пространство в стеке, пока стек не переполнится и не случится ошибка сегментации.[3]
Это рафинированный пример, и в реальном коде бесконечная рекурсия может появиться по двум причинам:
Частая причина бесконечной рекурсии — когда при каких-то крайних непроверенных обстоятельствах условие окончания рекурсии вообще не сработает.
int factorial (int n)
{
if (n == 0)
return 1;
return n * factorial(n - 1);
}
Программа уйдёт в бесконечную рекурсию при отрицательном n.
Многие языки делают оптимизацию, именуемую «хвостовая рекурсия». Рекурсия, находящаяся в конце функции, превращается в цикл и не расходует стека[4]. Если такая оптимизация сработает, вместо переполнения стека будет зацикливание.
Программист может написать рекурсию и ненамеренно — например, когда одну и ту же функциональность выполняют несколько перегруженных функций, и одна вызывает другую.
int Obj::getData(int index, bool& isChangeable)
{
isChangeable = true;
return getData(index);
}
int Obj::getData(int index)
{
bool noMatter;
return getData(index, noMatter);
}
В интерфейсных фреймворках наподобие Qt и VCL рекурсия может случиться, если в обработчике, например, изменения поля программист сам же это поле и изменит.
Уничтожить односвязный список можно таким кодом:
void destroyList(struct Item* it)
{
if (it == NULL)
return;
destroyList(it->next);
free(it);
}
Этот алгоритм, если список не испорчен, теоретически выполнится за конечное время, затребовав при этом O(n) стека. Разумеется, при длинном списке программа откажет. Возможные решения:
Третья большая причина переполнения стека — одноразовое выделение огромного количества памяти крупными локальными переменными. Многие авторы рекомендуют выделять память, превышающую несколько килобайт, в «куче», а не на стеке.[6]
Пример на Си:
int foo() {
double x[1000000];
}
Массив занимает 8 мегабайт памяти; если в стеке нет такого количества памяти, случится переполнение.
Всё, что уменьшает эффективный размер стека, увеличивает риск переполнения. Например, потоки обычно берут стека меньше, чем основная программа — поэтому программа может работать в однопоточном режиме и отказывать в многопоточном. Работающие в режиме ядра подпрограммы часто пользуются чужим стеком, поэтому при программировании в режиме ядра стараются не применять рекурсию и большие локальные переменные.[7][8]
Данная страница на сайте WikiSort.ru содержит текст со страницы сайта "Википедия".
Если Вы хотите её отредактировать, то можете сделать это на странице редактирования в Википедии.
Если сделанные Вами правки не будут кем-нибудь удалены, то через несколько дней они появятся на сайте WikiSort.ru .