Запутался насчет возвращаемого значения функции

#include<iostream>
using namespace std;
int Fun(int x)
{
    int sum=1;
    if(x>1)
        sum=x*Fun(x-1);
    else
        return sum;
}
int main()
{
    cout<<Fun(1)<<endl;
    cout<<Fun(2)<<endl;
    cout<<Fun(3)<<endl;
    cout<<Fun(4)<<endl;
    cout<<Fun(5)<<endl;
}

Эта функция предназначена для вычисления факториала целого числа. В ветке x>1, нет возвращаемого значения для функции Fun, Так что эта функция не должна возвращать правильный ответ.

Но когда fun(4) или некоторые другие примеры проверены, правильные ответы получены неожиданно. Зачем?

Код ассемблера этой функции (вызов Fun(4)):

0x004017E5  push   %ebp
0x004017E6  mov    %esp,%ebp
0x004017E8  sub    $0x28,%esp
0x004017EB  movl   $0x1,-0xc(%ebp)
0x004017F2  cmpl   $0x1,0x8(%ebp)
0x004017F6  jle    0x40180d <Fun(int)+40>
0x004017F8  mov    0x8(%ebp),%eax
0x004017FB  dec    %eax
0x004017FC  mov    %eax,(%esp)
0x004017FF  call   0x4017e5 <Fun(int)>
0x00401804  imul   0x8(%ebp),%eax
0x00401808  mov    %eax,-0xc(%ebp)
0x0040180B  jmp    0x401810 <Fun(int)+43>
0x0040180D  mov    -0xc(%ebp),%eax
0x00401810  leave
0x00401811  ret

Может быть, это причина: значение суммы сохраняется в регистре eaxи возвращаемое значение сохраняется в eax тоже так Funверните правильный результат.

6 ответов

Решение

Обычно регистр EAX используется для хранения возвращаемого значения, а также для других целей.

Поэтому все, что было загружено в этот регистр непосредственно перед возвратом функции, будет возвращаемым значением, даже если вы этого не собираетесь делать.

Вы можете использовать опцию -S, чтобы сгенерировать ассемблерный код и посмотреть, что случилось с EAX прямо перед инструкцией "ret".

Когда ваша программа перейдет в if условие, нет оператора возврата завершить функцию. Число, которое вы получили, является результатом неопределенного поведения.

int Fun(int x)
{
    int sum=1.0;
    if(x>1)
        sum=x*Fun(x-1);
    else
        return sum;

    return x; // return something here
}

Как указано в комментарии, это неопределенное поведение. С g ++ я получаю следующее предупреждение.

warning: control reaches end of non-void function [-Wreturn-type]

В Visual C++ предупреждение превращается в ошибку по умолчанию

error C4716: 'Fun' : must return a value

Когда я отключил предупреждение и запустил полученный исполняемый файл, Fun(4) дал мне 1861810763,

Так почему же он может работать под g ++? Во время компиляции условные операторы превращаются в тесты и переходы (или gotos). Функция должна что-то возвращать, и самый простой код для компилятора заключается в следующем.

int Fun(int x)
{
    int sum=1.0;
    if(!(x>1))
        goto RETURN;
    sum=x*Fun(x-1);
RETURN:
    return sum;
}

Это соответствует вашей разборке.

Конечно, вы не можете полагаться на неопределенное поведение, как показано поведением в Visual C++. По этой причине во многих магазинах действует политика, в соответствии с которой предупреждения считаются ошибками (также, как предлагается в комментарии).

Из стандартов C:

Выпуск из конца функции эквивалентен возврату без значения; это приводит к неопределенному поведению в функции, возвращающей значение.

Ваша ситуация такая же, как эта:

int fun1(int x)
{
    int sum = 1;
    if(x > 1)
        sum++;
    else
        return sum;

}
int main()
{
    int b = fun1(3);
    printf("%d\n", b);
    return 0;
}

Он печатает 2 на моей машине. Это называется соглашением и архитектурой. Возвращаемое значение является результатом последней оценки выражения, сохраненной в регистре eax.

В вашем коде есть несколько ошибок:

  • у вас есть int присвоение значения 1.0 (которое будет неявно приведено / преобразовано), не ошибка как таковая, но неэлегатная.
  • у вас есть оператор возврата внутри условия, так что вы получите возврат только тогда, когда if правда

Если вы исправите проблему с возвратом, удалив остальное, все будет хорошо:)

Что касается того, почему он работает с 4 в качестве входных данных, то это случайный случай / какое-то свойство вашей среды, так как код, который вы опубликовали, должен быть не в состоянии функционировать, так как всегда будет случай при вычислении факториалов для положительного целого где x = 1 и возврат не будет сгенерирован.

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

int factorial(int x){ return (x>1) ? (x * factorial(x-1)) : 1;}

это функция, которую я использую для своих факториалов и которая использовалась в библиотеке последние 30 лет или около того (с моих дней C):)

Просто удали else из вашего кода:

int Fun(int x)
{
    int sum=1;
    if(x>1)
        sum=x*Fun(x-1);

    return sum;
}
Другие вопросы по тегам