2016-04-12 3 views
1

Итак, мой друг берет один из своих первых классов CS и упоминает, что он использует рекурсию в своей первой программе. Он отправляет мне код ниже. Сразу после битвы я заметил, что он не улавливает возвращаемую стоимость своего рекурсивного звонка, и я предположил, что это не сработает. Но он настаивает на том, что он работает, поэтому я пробую его программу, и, к моему удивлению, он функционирует точно так, как ожидалось. Игнорируя тот факт, что это тупой способ добраться из точки А в точку Б, почему это работает?Почему эта рекурсивная функция работает?

Я играл с тем, что он послал мне, и добавил cout после утверждения if. Кроме того, первый фрагмент кода и второй фрагмент идентичны.

Если я введите следующее для первой программы, вот что я получаю ...

Введите номер: 10

Вы вошли: 10 Правильно ли это? (Y/N): N

Введите номер: 12

Вы ввели: 12 Правильно ли это? (Y/N): Y

основной() = 12

И потом, если я делаю то же самое со второй программой, вот что я получаю ...

Введите Номер: 10

Вы ввели: 10 Правильно ли это? (Y/N): N

Введите номер: 12

Вы ввели: 12 Правильно ли это? (Y/N): Y

основной() = 6300096

Что происходит !?

#include <iostream> 
#include <cstring> 
#include <cctype> 

using namespace std; 

int getNum() 
{ 
    cout << "Enter a Number: "; 
    int x; 
    cin >> x; 
    cin.ignore(100, '\n'); 

    while(x < 0) { 
     cout << "Please enter amount greater than 0: "; 
     cin >> x; 
     cin.ignore(100, '\n'); 
    } 

    cout << "You entered: " << x << " Is this correct? (Y/N): "; 
    char response; 
    cin >> response; 
    cin.ignore(100, '\n'); 

    if (response != 'Y') { 
     getNum(); 
    } else { 
     return x; 
    } 
} 

int main() { 

    cout << "\nmain() = " << getNum() << endl; 

    return 0; 
} 

Единственная разница между верхней и нижней является cout заявление после того, если заявление.

#include <iostream> 
#include <cstring> 
#include <cctype> 

using namespace std; 

int getNum() 
{ 
    cout << "Enter a Number: "; 
    int x; 
    cin >> x; 
    cin.ignore(100, '\n'); 

    while(x < 0) { 
     cout << "Please enter amount greater than 0: "; 
     cin >> x; 
     cin.ignore(100, '\n'); 
    } 

    cout << "You entered: " << x << " Is this correct? (Y/N): "; 
    char response; 
    cin >> response; 
    cin.ignore(100, '\n'); 

    if (response != 'Y') { 
     getNum(); 
    } else { 
     return x; 
    } 
    cout << "returning... " << x; 
} 

int main() { 

    cout << "\nmain() = " << getNum() << endl; 

    return 0; 
} 
+3

В 'if (response! = 'Y')', не следует ли вам возвращать getNum(); '? Когда вы пишете это так, если 'response! = 'Y'', вы никогда ничего не возвращаете, поэтому тот факт, что ваша первая программа работает, - это в основном удача. – tforgione

+1

Да, вы правы, но я спрашиваю о части удачи. –

+0

Мой плохой, не понял ваш вопрос. – tforgione

ответ

6

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

Формально код Неопределенное поведение, не выполняя return заявление, в некоторых вызовах getNum, но то, что происходит, вероятно, это:

  1. getNum() называется, пользователь отвечает N.

  2. getNum() называет себя рекурсивно, ответ пользователя Y.

  3. getNum() исполняет return x;. При типичной реализации C++ это помещает возвращаемое значение в регистр, давайте назовем его R.

  4. Выполнение возвращает до getNum() (исходного вызова), который теперь возвращается казнью, проходящей через конец функции, не return.

  5. Код вызова находит значение в регистре R, как и ожидалось.

Таким образом, он может “ ” работу.

Но это формально Неопределенный Поведение, а также с некоторыми другими компиляторами и/или параметрами, это может не сработать.

+1

Это также объясняет, почему он не работает с 'cout <<" return ... "<< x;' - этот код сам использует «R» .... –

+0

@TonyD: Хорошая точка, спасибо. :) –

2

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

Я думаю, что они просто забыли return до getNum(). Он будет работать и не будет загрязнять стек, потому что если оптимизация хвостовой рекурсии.

Этот код является странным для первых классов CS.