как программа печатает 10 операторов? - PullRequest
1 голос
/ 06 октября 2011

Эта программа рекурсивно вызывает функцию tester.

#include <iostream>
 void tester();
 int i = 1;
 int k = 1;

 int main() {
   tester();
 }

 void tester() {
  while(i++ < 10)
    tester();
  std::cout << "called " << k++ << " times" << std::endl;
 }

Я удивлен этим выводом:

called 1 times
called 2 times
called 3 times
called 4 times
called 5 times
called 6 times
called 7 times
called 8 times
called 9 times
called 10 times

и это потому, что я так понимаю эту программу :

После первого вызова тестера с основного он входит в цикл. Первый оператор цикла снова вызывает функцию tester, и это продолжается. После 10-кратного цикла оператор, следующий за циклом while, должен работать , т.е. только один раз . Таким образом, вывод должен быть: called 1 times. Но на самом деле этого не происходит! Зачем ? Как работает эта программа?

Ответы [ 4 ]

2 голосов
/ 06 октября 2011

тестер выполняется десять раз, поэтому курс std :: cout также используется десять раз.

Порядок печати фактически меняется на порядок вызовов, поскольку последний вызов тестера возвращается первым из-за рекурсии.

EDIT: Попробуйте этот код, может быть, он поможет вам понять (извините, это не очень красиво, просто быстрый взлом):

#include <iostream>
void tester();
void print_indent(int cnt);
int i = 1;
int k = 1;
int recursion_level = 0;
int call_num = 0;

int main() {
    tester();
}

void tester() {
    int my_call = call_num++;
    print_indent(recursion_level);
    std::cout << "start of tester " << my_call << std::endl;
    recursion_level++;
    while(i++ < 10)
        tester();
    print_indent(recursion_level);
    std::cout << "called " << k++ << " times" << std::endl;
    recursion_level--;
    print_indent(recursion_level);
    std::cout << "end of tester " << my_call << std::endl;
}

void print_indent(int cnt) {
    for (int i = 0; i < cnt; i++)
        std::cout << "  ";
}
2 голосов
/ 06 октября 2011

Тело while выполняется только один раз на каждой рекурсивной глубине.while ложно, когда i равно 10, что будет, когда tester () вызывается 10 раз рекурсивно.Поскольку i объявлен глобальным, обновление i++ будет видно для каждого вызова tester ().

На последней рекурсивной глубине, когда условие while ложно, последнее tester ()вызов вернется к своей предыдущей глубине.В этот момент следующая итерация цикла while будет ложной, поскольку i равно 10.Оператор cout будет встречаться после завершения каждого цикла while, который будет последовательно печатать значения k, увеличивающиеся на каждой рекурсивной глубине, по мере отката рекурсии.

Вручную отследите, что происходитчтобы понять вещи.

ОБНОВЛЕНИЕ

Посмотрите на результаты выполнения.Особо обратите внимание на параметр d, который обозначает рекурсивную глубину.На каждой глубине цикл while повторялся один раз, до последнего ", в то время как истина, тестер называл" output.На глубине 10 значение while является ложным, поскольку оно равно 10, и в первый раз он возвращает элемент управления к своему предыдущему уровню глубины (первый «возврат назад», когда i равен 10).После возврата управление возвращается в тело цикла while последнего уровня, из которого была вызвана функция (из которой она только что вернулась), и следующая итерация этого цикла будет ложной (i является глобальным и10 или более), поэтому это также возвращает.Точно так же на каждой рекурсивной глубине 2-я итерация цикла while имеет значение false и продолжает возвращаться.Проверьте выход.

i   k  d
 1  1  0, call from main
 2  1  1, while is true, tester called
 3  1  2, while is true, tester called
 4  1  3, while is true, tester called
 5  1  4, while is true, tester called
 6  1  5, while is true, tester called
 7  1  6, while is true, tester called
 8  1  7, while is true, tester called
 9  1  8, while is true, tester called
10  1  9, while is true, tester called
11  1 10, returning back, print k    // this step the while is false in depth 10
12  2  9, returning back, print k    // from now on, as the recursion rolls back
13  3  8, returning back, print k    // the second iteration at each recursive 
14  4  7, returning back, print k    // depth will be executed, and each while
15  5  6, returning back, print k    // condition will be false, therefore it will
16  6  5, returning back, print k    // not call tester anymore and return the control
17  7  4, returning back, print k    // to the previous level. NOICE the `d' parameter
18  8  3, returning back, print k
19  9  2, returning back, print k
20 10  1, returning back, print k

А вот и тестовый код.Я надеюсь, что описание теперь очень понятно (?).Анализ это поможет.

#include <stdio.h>

int i = 1;
int k = 1;

void tester (int d)
{
  while (i++ < 10)
  {
    printf ("%2d %2d %2d, while is true, tester called\n", i, k, d);
    tester (d+1);
  }
  printf ("%2d %2d %2d, returning back, print k\n", i, k++, d);
}

int main (void)
{
  int depth = 0;
  printf ("i   k  d\n");
  printf ("%2d %2d %2d, call from main\n", i, k, depth);
  tester (depth + 1);

  return 0;
}
1 голос
/ 06 октября 2011

В этом суть рекурсии.Когда tester() вызывает себя, он создает стек вызовов, а затем печатает сообщение, когда возвращается каждая функция.Каждый раз, когда тестер возвращается, он печатает сообщение, что произойдет 10 раз из-за цикла while.Я визуализирую это так:

i    k    
---  ---  ---
 1    1  + tester()
 2    1  |   + tester()
 3    1  |   |   + tester()
 4    1  |   |   |   + tester()
 5    1  |   |   |   |   + tester()
 6    1  |   |   |   |   |   + tester()
 7    1  |   |   |   |   |   |   + tester()
 8    1  |   |   |   |   |   |   |   + tester()
 9    1  |   |   |   |   |   |   |   |   + tester()
10    1  |   |   |   |   |   |   |   |   |   + tester()
11    1  |   |   |   |   |   |   |   |   |   + "called 1 times"
12    2  |   |   |   |   |   |   |   |   + "called "2" times"
13    3  |   |   |   |   |   |   |   + "called 3 times"
14    4  |   |   |   |   |   |   + "called 4 times"
15    5  |   |   |   |   |   + "called 5 times"
16    6  |   |   |   |   + "called 6 times"
17    7  |   |   |   + "called 7 times"
18    8  |   |   + "called 8 times"
19    9  |   + "called 9 times"
20   10  + "called 10 times"
1 голос
/ 06 октября 2011

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

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

...