Если рекурсия слишком глубокая, программе не хватает памяти стека. Это называется переполнением стека.
int modulo(int n, int m)
{
if (n < m) return n;
else return modulo(n - m, m);
}
Например, modulo(1000000, 2)
вызывает modulo(999998, 2)
, что вызывает modulo(999996, 2)
и так далее до тех пор, пока в конце modulo(0, 2)
не будет 500001 активных modulo
функций в стеке. Два целых числа, адрес возврата и указатель кадра, должны занимать не менее 16 байт на вызов функции в любой разумной системе. Всего 8 мегабайт стекового пространства, что обычно превышает максимальный размер стека.
Каждый вызов функции должен ждать результатов следующей функции, пока он не завершит свой расчет и не вернется. До его возвращения в стеке должны были храниться все переменные, параметры и адрес возврата. Адрес возврата - это место в программе, где программа должна возобновить работу после оператора return
.
Эти вызовы заполняют стек до тех пор, пока не будет достигнут его максимальный предел и программа не выйдет из строя.
В некоторых случаях компилятор может преобразовать рекурсию в цикл. В этом случае, поскольку рекурсия находится в операторе return
, она может просто goto
перейти к началу функции, не выполняя вообще никакого вызова. Это называется Оптимизация хвостового вызова :
int modulo(int n, int m)
{
start:
if (n < m) return n;
else {
n -= m;
goto start;
}
}
Если бы вы включили оптимизацию (-O2 в clang или G ++ или режим релиза в Visual C ++), не было бы сбоя, так как компилятор создал бы цикл вместо рекурсии. Чтобы избежать сбоя, просто включите оптимизацию.
Обратите внимание, что компилятор не требуется оптимизировать это, и не всегда может. Вот почему не рекомендуется иметь такую глубокую рекурсию.