没有递归调用的递归?

时间:2011-06-16 10:43:38

标签: c linux gcc recursion

在/ 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 个答案:

答案 0 :(得分:22)

甜。 ;)

这是非常不可移植的代码,仅适用于x86。它正在做的是更改堆栈上的返回地址,以便在in>1时,函数不返回 {/ 1}}指令之后的指令,而是返回到调用指令本身。 x86上的调用指令是五个字节(一个操作码加上调用目标的4字节地址),因此需要从返回地址中减去五个字节。

call

只是一种混淆的说法

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

if(in>1) *(&in-1)-=5; 是返回地址在堆栈中的位置。

答案 1 :(得分:8)

它破坏了堆栈上的返回地址,导致递归发生。

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

&in-1可能是推送的返回地址。其余的只是令人不快的魔力。

答案 2 :(得分:0)

如前所述*(&amp; in-1)是返回地址,5(我猜)是函数的大小(用单词?),它减去两者以得到函数开始的地址

编辑:由于我认为对返回地址的更改是永久性的,我不明白为什么在&gt;中减去5 1;

EDIT2:见评论

汇编程序返回指令只跳转到堆栈顶部的地址。

由于调用约定,参数被推送到堆栈并由调用者从中移除。由于只有一个真正的调用没有递归,所以参数和返回地址(指针)在迭代之间共享。

它更像是一个循环,而不是一个递归函数。