Лекция 18. Рекурсия

TODO: картинка про адрес возврата

  • TODO Напоминание про кадр стека
  • Перед тем, как функция f вызовет функцию g, она записывает в конец своего кадра стека адрес той своей инструкции, которая должна быть выполнена после выхода из g.
  • При вызове функции f из самой себя этот механизм работает точно так же, и при возврате из такого вызова (он называется рекурсивным) все переменные восстановят свои значения, которые были до рекурсивного вызова, выполнение будет продолжено со следующей инструкции.
int fact(int n)
{
    int i, f = 1;
    for (i = 2; i <= n; ++i) {
        f *= i;
    }
    return f;
}

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

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

double bisection_rec(double a, double b, double eps)
{
    double c = (a + b) / 2.0;
    if (fabs(a - b) < eps) {
        return c;
    }
    double fc = f(c);
    if (f(a) * fc <= 0) {
        return bisection_rec(a, c, eps);
    } else if (f(b) * fc <= 0) {
        return bisection_rec(c, b, eps);
    } else {
        printf("Условия корректноси не выполнены\n");
        exit(1);
    }
}

Числа Фибоначчи

\[f_0 = 0, \quad f_1 = 1, \quad f_n = f_{n-1} + f_{n-2}\]
int fib_rec(int n)
{
    if (0 == n) {
        return 0; // Терминальная ветвь
    }
    if (1 == n) {
        return 1; // Терминальная ветвь
    }
    return fib_rec(n - 1) + fib_rec(n - 2); // Рекурсивная ветвь
}

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

int fib_nums[40]; // Глобальная переменная для хранения чисел Фибоначчи.
int fib_fill = 0;

int fib_rec2(int n)
{
    if (n < 2) {
        return n;
    }
    if (n < fib_fill) {
        return fib_nums[n];
    }
    fib_nums[n] = fib_rec2(n - 1) + fib_rec2(n - 2);
    fib_fill = n + 1;
    retutrn fib_nums[n];
}

Статические переменные

static int a = 1;
  • Доступны только той функции, в которой определены
  • сохраняют своё значение между вызовами функций и на протяжении всего времени работы располагаются в одном и том же участке памяти.
  • инициализируются один раз (при первом запуске функции)
int fib_rec3(int n)
{
    static int fib_nums[40]; // Статическая переменная
                // для хранения чисел Фибоначчи.
    static int fib_fill = 0;
    if (n < 2) {
        return n;
    }
    if (n < fib_fill) {
        return fib_nums[n];
    }
    fib_nums[n] = fib_rec3(n - 1) + fib_rec3(n - 2);
    fib_fill = n + 1;
    retutrn fib_nums[n];
}

TODO: пример решения чего-нибудь.