Рекурсивная функция с двумя вызовами

#c #recursion

Вопрос:

Здесь у меня есть код о рекурсии из geeksforgeeks.com

 #include<stdio.h>
void fun(int x)
{
  if(x > 0)
  {
     fun(--x);
     printf("%dt", x);
     fun(--x);
  }
}
 
int main()
{
  int a = 4;
  fun(a);
  return 0;
}
 

Я сам действительно теряюсь, я не могу понять принцип работы этой функции. Насколько я знаю, во-первых, рекурсивный каждый вызов функции хранится в стеке и запускается после достижения базового варианта. Однако я не могу понять, как это работает после функции printf, которую вызывает второй вызов. Кто-нибудь может мне объяснить, пожалуйста? Я очень рад, если это так.
Заранее спасибо за ваш прекрасный вклад.

Комментарии:

1. Просто пройдитесь по нему с помощью отладчика или добавьте еще несколько инструкций печати, чтобы показать, что происходит.

Ответ №1:

Переменная x в fun(x) является локальной, и первый вызов-x-1, второй-x-2

Рассмотрим простейший случай

 fun(1)
 

В результате вызывается fun(0) и fun(-1)

 fun(2)
 

Приводит к вызову fun(1) и fun(0), а поскольку 1 > 0, fun(1) также вызовет fun(0) и fun(-1), прежде чем fun(2) вызовет fun(0)

Вот частичный стек вызовов, с отступом в соответствии с тем, какая функция вызывает

 fun(4)
  fun(3)
    fun(2)
      fun(1)
        fun(0)
        print
        fun(-1)
      print
      fun(0)
    fun(1)
      fun(0)
      print
      fun(-1)
    print
    fun(0)
  fun(2)
...
      
 

Ответ №2:

Приведенная ниже подробная информация иллюстрирует глубину вызова с использованием отступов и показывает вызовы для fun и printf для иллюстрации последовательности выполнения и вывода на печать:

 Line 15: fun(4)                       --> depth 1 
    Line 6: x=3, fun(3)               --> depth 2
        Line 6: x=2, fun(2)           --> depth 3
            Line 6: x=1, fun(1)       --> depth 4
                Line 6: x=0, fun(0)   --> depth 5, do nothing amp; return
                Line 7: printf        --> "0"
                Line 8: x=-1, fun(-1) --> depth 5, do nothing amp; return
                Return                --> depth 3
            Line 7: printf            --> "1"
            Line 8: x=0, fun(0)       --> depth 4, do nothing amp; return
            Return                    --> depth 2
        Line 7: printf                --> "2"
        Line 8: x=1, fun(1)           --> depth 3
            Line 6: x=0, fun(0)       --> depth 4, do nothing amp; return
            Line 7: printf            --> "0"
            Line 8: x=-1, fun(-1)     --> depth 4, do nothing amp; return
            Return                    --> depth 2
    Line 7: printf                    --> "3"
    Line 8: x=2, fun(2)               --> depth 2
        Line 6: x=1, fun(1)           --> depth 3
            Line 6: x=0, fun(0)       --> depth 4, do nothing amp; return
            Line 7: printf            --> "0"
            Line 8: x=-1, fun(-1)     --> depth 4, do nothing amp; return
            Return                    --> depth 2
        Line 7: printf                --> "1"
        Line 8: x=0, fun(0)           --> depth 3, do nothing amp; return
        Return                        --> depth 1
    Return                            --> depth 0
 

Ответ №3:

Прежде всего, я рекомендую вам прочитать книгу о рекурсии, например, книги Розсы Питера или Даниэля Фридмана («Маленький интриган«), или лучше пройти какой-нибудь курс первого курса/mooc введения. После этого вы сможете изучать более продвинутые предметы.

У вас есть этот код:

 void fun(int x)
{
  if(x > 0)
  {
     fun(--x);
     printf("%dt", x);
     fun(--x);
  }
}
 

Предположим, вы называете забавой(4).

В этом случае у вас есть сигма-рекурсивный оператор (также называемый примитивной рекурсией). Для рекурсии нужны некоторые предельные случаи. В данном случае это простая линейная рекурсия только в том случае, если «счетчик достигает нулевого значения» (то же самое, что и в определении арифметики Пеано).

Если он достигнет 0, он завершен. В противном случае он вызывает сам себя с числом, уменьшенным на 1 по отношению к предельному регистру, и собирает еще 2 вызова:

  printf("%dt", x);
 fun(--x);
 

Последний вызов находится в хвостовой позиции, поэтому он ничего не делает, он изменил локальную переменную, которая больше не используется. Таким образом, ваша функция сводится к этому:

 void fun(int x)
{
  if(x > 0)
  {
     fun(--x);
     printf("%dt", x);
  }
}
 

Как говорится в других ответах, эта функция будет стекировать некоторые вызовы, и я больше не воспроизводю стек, который присутствует в других ответах.

Практически,

  • он называет себя для f(4),
  • тогда f(3),
  • тогда f(2),
  • тогда f(1),
  • тогда f(0) является предельным случаем,
  • затем он возвращается к набору printf из f(1) и ПЕЧАТАЕТ 1,
  • затем он возвращается к набору printf из f(2) и ПЕЧАТАЕТ 2,
  • затем он возвращается в сложенный printf из f(3) и ПЕЧАТАЕТ 3,
  • затем он возвращается в сложенный printf из f(4) и ПЕЧАТАЕТ 4.