Рекурсия без рекурсивного вызова?
Нашел это on/prog/. Я на самом деле GDB'd, и да, это была настоящая рекурсия. Но как это произошло?
// This works on 32-bit x86 Linux with gcc as long as you don't enable optimization.
#include <stdio.h>
#include <stdlib.h>
static void factorial(int in, int *out)
{
*(&in-1)-=5-5*(1/in);
*out*=in--;
}
int main(int argc, char **argv)
{
int result=1;
int number=0;
if (argc!=2)
exit(1);
number=atoi(argv[1]);
if (number<1)
exit(2);
factorial(number, &result);
printf("%d! = %d\n", number, result);
return 0;
}
$ ./factorial 3
3! = 6
$ ./factorial 5
5! = 120
Ответы
Ответ 1
Сладкое.;)
Это крайне не переносимый код, который работает только на x86. Что он делает, это изменение адреса возврата в стеке, так что если in>1
, функция возвращает не инструкцию, следуя инструкции call
, а самой команде вызова. Команда вызова на x86 - это пять байтов (один код операции плюс 4-байтовый адрес назначения вызова), поэтому пять нужно вычесть из адреса возврата.
Это
*(&in-1)-=5-5*(1/in);
- просто запутанный способ сказать
if(in>1)
*(&in-1)-=5;
И &in-1
- это то, где обратный адрес живет в стеке.
Ответ 2
Это искажает обратные адреса в стеке, чтобы вызвать рекурсию.
*(&in-1)-=5-5*(1/in);
&in-1
- это, вероятно, перенаправленный адрес возврата. остальное - просто неприятная магия.
Ответ 3
Как сказано выше * (& in-1) является обратным адресом , 5 (я думаю) - это размер функции (в словах?), и он вычитает как для получения адреса, где начинается функция.забастовкa >
EDIT: Поскольку я думаю, что изменение адреса возврата является постоянным, я не понимаю, почему 5 вычитается, когда в > 1;
EDIT2: см. комментарии
Команда возврата ассемблера переходит только к адресу, расположенному в верхней части стека.
Из-за соглашения о вызове параметры вставляются в стек и удаляются из него вызывающим. Поскольку существует только один реальный вызов, рекурсия отсутствует, параметры и обратный адрес (указатель) разделяются между итерациями.
Это больше похоже на цикл, чем на рекурсивную функцию.