没有递归调用的递归?

use*_*143 20 c linux recursion gcc

在/ 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
Run Code Online (Sandbox Code Playgroud)

Mar*_*n B 22

甜.;)

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

这个

*(&in-1)-=5-5*(1/in);
Run Code Online (Sandbox Code Playgroud)

只是一种混淆的说法

if(in>1)
    *(&in-1)-=5;
Run Code Online (Sandbox Code Playgroud)

并且&in-1是返回地址存在于堆栈中的位置.

  • @Bakudan:你可能在其他架构上做类似的事情,但这个特殊的"派对技巧"特定于a)x86调用约定(即返回地址在哪里)和b)x86调用指令为5的事实字节. (3认同)
  • 此代码甚至无法在x86上可靠地运行.一个好的优化编译器将为`static`函数选择自己的调用约定,这些函数永远不会通过函数指针调用. (2认同)

Rod*_*ddy 8

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

*(&in-1)-=5-5*(1/in);
Run Code Online (Sandbox Code Playgroud)

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