Запутался насчет возвращаемого значения функции
#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;
}