Как избежать переполнения стека?

или Вы могли просто записать функцию питания с рекурсией как добавленная премия

int power(int x, int y){
      if(y == 0)
        return 1;
     return (x * power(x,y-1) );
    }

да, да я знаю, что это - менее эффективная сложность пространства и времени, но рекурсия является просто большим количеством забавы!!

5
задан Community 23 May 2017 в 12:11
поделиться

7 ответов

Run вызывает Run. Это бесконечная рекурсия.

    class StackOver : MarshalByRefObject
    {
        public void Run()
        {
            Run(); // Recursive call with no termination
        }
    }
8
ответ дан 18 December 2019 в 07:55
поделиться

Единственный способ избежать переполнения стека с рекурсивными функциями - иметь четкое условие выхода, которое в конечном итоге будет выполнено независимо от ввода. Либо вы определяете максимальную глубину и прекращаете выполнение рекурсивных вызовов, как только достигнете ее, либо вы убедитесь, что исследуемые вами данные являются конечными (и в разумных пределах), либо их комбинация.

1
ответ дан 18 December 2019 в 07:55
поделиться

Хорошо. Неважно использовать CSharpCodeProvider или нет. Я загружаю сборку с помощью Reflection в другом домене. Думаю, домены были созданы из соображений безопасности. Как я могу защитить приложение от завершения ???

using System;
using System.Runtime.Remoting;
namespace TestStackOverflow
{
    class Program
    {
        class StackOver : MarshalByRefObject
        {
            public void Run()
            {
                Run();
            }
        }

        static void Main(string[] args)
        {
        AppDomain domain = AppDomain.CreateDomain("new");

        ObjectHandle handle = domain.CreateInstance(typeof (StackOver).Assembly.FullName, typeof (StackOver).FullName);
        if (handle != null)
        {
            StackOver stack = (StackOver) handle.Unwrap();
            stack.Run();
        }

    }
}
}
0
ответ дан 18 December 2019 в 07:55
поделиться

Каждый раз, когда вы вызываете метод foo из панели методов, bar добавляется в стек вызовов. Стек вызовов используется для отслеживания того, где был код до вызова метода, чтобы он мог вернуться туда, когда foo будет выполнен.

Следующая рекурсивная функция

int Factorial(int n) {
    if (n == 0) { return 1; }
    return n * Factorial(n - 1);
}

после нескольких рекурсий вызова Factorial (5) вызов стек будет выглядеть так:

Factorial(5) -> Factorial(4) -> Factorial(3) -> Factorial(2) -> Factorial(1)

В этот момент n равно 1, и поэтому функция перестает вызывать рекурсивный случай и вместо этого возвращает 1. Затем программа начинает откатывать стек вызовов, и все возвращается 120.

Без стека вызовов программа не знала бы, куда вернуться, когда закончит выполнение метода.

Теперь предположим, что этого базового случая не было, и он просто выглядел так:

int Factorial(int n) {
    return n * Factorial(n - 1);
}

После нескольких рекурсий вызова Factorial (5) стек вызовов будет выглядеть следующим образом:

Factorial(5) -> Factorial(4) -> Factorial(3) -> Factorial(2) -> Factorial(1) -> Factorial(0) -> Factorial(-1) -> Factorial(-2) -> Factorial(-3) -> Factorial(-4) -> Factorial(-5) -> Factorial(-6) -> Factorial(-7) -> Factorial(-8) -> Factorial(-9) -> Factorial(-10) -> Factorial(-11) -> Factorial(-12) -> Factorial(-13) -> Factorial(-14) -> Factorial(-15) etc…

Поскольку нет точки, в которой код перестает вызывать себя, он будет продолжаться вечно, а стек вызовов будет расти и расти и растут, занимая все больше и больше памяти, пока она не превысит объем выделенной памяти, и возникнет исключение StackOverflow.

Есть два способа предотвратить это, лучший зависит от ситуации.

1 Предоставьте базовый вариант. Убедитесь, что в конечном итоге достигается какое-то условие, которое не позволяет функции вызывать саму себя. В факториальном случае это n == 1, но могло случиться так, что прошло определенное количество времени, что он повторял определенное количество раз, что некоторый результат некоторых вычислений находится в каких-то границах, что угодно. Пока он прекращает повторное выполнение до того, как стек станет слишком большим.

2 Удалите рекурсию и перезапишите ее без нее. Любой рекурсивный алгоритм можно переписать как нерекурсивный алгоритм. Это может быть не так чисто и элегантно, но это можно сделать. В аргументе факториала это может быть что-то вроде:

int Factorial(int n) {
    int result = 1;

    for (int i = 0; i < n; i += 1) {
        result *= n;
    }

    return result;
}

Если цель состоит в том, чтобы постоянно запускать одну и ту же функцию снова и снова, то вы можете переписать рекурсивный

void Foo() {
    // Some code
    Foo();
}

как

void Foo() {
    while (true) { // Some code }
}
0
ответ дан 18 December 2019 в 07:55
поделиться

StackOverflow indicates that your recursion is going too deep and the stack is running out of memory. For example:

public class StackOver
{
    public void Run() 
    { 
        Run(); 
    }
}

This will result in a stack overflow because StackOver::Run() will be called over and over until there is no memory left.

I suspect in your case, you may be missing a termination condition or you are running too many recursion iterations.

If you are trying to keep the application running, try:

namespace TestStackOverflow
{
    class Program
    {
        class StackOver : MarshalByRefObject
        {
            public bool Run()
            {
                return true; // Keep the application running. (Return false to quit)
            }
        }

        static void Main(string[] args)
        {
            // Other code...

            while (stack.Run());
        }

    }
}
10
ответ дан 18 December 2019 в 07:55
поделиться

If recursion causes a stack overflow, then the problem is not related to compiling the class -- a recursive function needs a terminating condition, because C# doesn't (usually) optimize tail calls.

1
ответ дан 18 December 2019 в 07:55
поделиться

I dont have a good background of CSharpCodeProvider but I know that recursively implemented algorithm could be implemented with a loop

0
ответ дан 18 December 2019 в 07:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: