Рекурсия без рекурсивного вызова? - PullRequest
20 голосов
/ 16 июня 2011

Нашел это на / prog /. Я на самом деле GDB сделал это, и да, это была действительно рекурсия. Но как это случилось?

// 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

Ответы [ 3 ]

22 голосов
/ 16 июня 2011

Сладкое. ;)

Это крайне непереносимый код, который работает только на x86. Что он делает, так это изменяет адрес возврата в стеке, так что если in>1, функция возвращается не к инструкции , следующей за * call, а к самой инструкции вызова. Команда вызова в x86 составляет пять байтов (один код операции плюс 4-байтовый адрес получателя вызова), поэтому из обратного адреса необходимо вычесть пять.

Это

*(&in-1)-=5-5*(1/in);

это просто запутанный способ сказать

if(in>1)
    *(&in-1)-=5;

И &in-1 - это адрес возврата в стеке.

8 голосов
/ 16 июня 2011

Повреждает адреса возврата в стеке, вызывая рекурсию.

*(&in-1)-=5-5*(1/in);

&in-1, вероятно, заданный адрес возврата. остальное просто неприятная магия.

0 голосов
/ 16 июня 2011

Как уже говорилось ранее * (& in-1) - это адрес возврата , 5 (я полагаю) - это размер функции (в словах?), И он вычитает оба, чтобы получить адрес, с которого начинается функция.

РЕДАКТИРОВАТЬ: Поскольку я думаю, что изменение обратного адреса является постоянным, я не понимаю, почему 5 вычитается при> 1;

РЕДАКТИРОВАТЬ 2: См.комментарии

Инструкция возврата ассемблера переходит только по адресу, который находится на вершине стека.

Из-за соглашения о вызовах параметры помещаются в стек и удаляются из неговызывающий абонент.Поскольку существует только один реальный вызов, рекурсии нет, параметры и адрес возврата (указатель) совместно используются итерациями.

Это больше похоже на цикл, чем на рекурсивную функцию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...