递归函数引用参数对工作栈大小影响的分析

Posted on 2006-08-28 18:41  樱木 阅读(406)  评论(0)   编辑  收藏  引用 所属分类:  算法与数据结构 

本文从递归函数参数的角度来分析,递归函数调用时递归工作栈中工作记录减肥的可能性。

用最简单的求阶层函数来举例:
n! = n*(n-1)*(n-2)***2*1

常见的递归实现如下:
int fun(int n)
{
      if( n<=1 ) return 1;
      return n*fun(n-1);
}
不用多说,这个实现下每次递归调用都会在当前层的工作记录中分配一个4字节(IBM兼容机)的空间来存放n的值。若栈的深度为DEPTH,那么一共需要DEPTH×4个字节存放参数。

那么能不能避免这些存储空间呢,我们来看看引用的情况:
int fun(int &n)
{
      if( n<=1 ) return 1;
      return n*fun(n-1);
}
这段代码编译不能通过,原因是n-1只是一个右值。这里简单介绍一下c++中的无名对象(变量)。c++中的无名对象与const常量有些相似,他们都只能做右值而不能做左值,区别是const常量是有地址的可以取地址,而无名对象一般是不可以的。这里n-1有点无名对象的味道,因为fun要求一个变量引用。由于n-1是右值,所以把参数类型改成int fun(const int & n)就可以了。

int fun(const int &n)
{
      if( n<=1 ) return 1;
      return n*fun(n-1);
}
现在运行通过了,但是栈空间需求仍然没有减少,因为引用的是无名对象,无名对象只是寄存器中的一个值,是没有RAM地址的。所以每递归调用一次,就会在工作记录中分配空间来存放n-1,实际效果相当于const int n = n-1;

那么换个思路。以上程序之所以如此是因为传给递归函数的不是n本身,现设计如下:
int fun(const int & n)
{
      cout<<(long)&n<<endl;
      if( n<=1 ) return 1;
      --n;
      return (n+1)*fun(n);
}
这个程序运行正确而且所有的n确实是最外面的那个n的引用(打印的地址均一致)。有些人会怀疑这个程序不能正确的求出n!。想想也对,当递归不断深入到最里层的时候n=1,递归回退的时候n仍然还是1,那结果不就成了1×2×2×2××××2了么??您可以试试看,结果可能出乎您的预料:计算出的确实是 n! 原因如下:
每调用一层时先计算n+1,n+1的结果保存在register中,进入下一层递归前编译器会把适当的register的值压栈以便日后恢复,这里某个寄存器就存放着运算的中间结果n+1。日后返回该层时虽然此时n确实是1,但是n+1的结果已经计算过,所以只是从栈顶取出n+1而非重新计算。作为思考,您可以试试看将(n+1)*fun(n)改成fun(n)*(n+1),它将得出错误的结果。

看来,递归调用函数使用引用参数和普通使用引用参数的非递归调用函数是有区别的:并不是使用了引用参数后就一定能够节省栈的空间,这取决于具体的递归函数(复杂。。。^_^)

当然,在递归函数中使用引用参数还有一种目的,当然是为了改变实参的数值罗。对于非内部类型的对象而言,使用引用往往可以既节省空间又同时可以修改实参的值,这里就不再举例了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 来传递数据? 可以使用STI库中的函数来操作,比如push和pop函数。在函数调用时,可以将作为参数传递给函数,这样函数就可以在中读取或写入数据。例如,下面的代码演示了如何将一个整数压入中,并在函数中读取它: ```lua local stack = {} table.insert(stack, 42) -- 将整数42压入中 function foo(stack) local value = table.remove(stack) -- 从中弹出一个值 print(value) -- 输出42 end foo(stack) -- 调用函数,并将作为参数传递 ``` 在这个例子中,我们使用table来模拟的行为。我们首先将整数42压入中,然后调用函数foo,并将作为参数传递。在函数中,我们使用table.remove函数中弹出一个值,并将它存储在变量value中。最后,我们输出这个值,它应该是42。 ### 回答2: STI库中的可以作为参数传递给其他函数或方法,以便在函数或方法的执行过程中可以访问和操作中的数据。通过将作为参数传递,我们可以将引用传递给函数或方法,从而使其拥有访问和修改数据的能力。 在将作为参数传递时,可以使用指针或引用的方式进行传递。指针是一种变量类型,它存储的是内存地址,通过指针可以间接访问和操作所指向的对象。而引用则是对变量的别名,它为传递的变量创建了一个新的名称,通过引用可以直接访问和修改所指向的对象。 在使用指针传递参数时,需要确保传递的指针指向有效的对象。在函数或方法中,可以通过解引用指针来访问和操作数据。例如,可以使用指针来获取大小、压入数据、弹出数据等操作。 在使用引用传递参数时,可以使用引用作为函数或方法的参数类型。通过引用传递参数,可以直接使用引用来访问和操作数据,无需使用额外的解引用操作。例如,可以使用引用来获取大小、压入数据、弹出数据等操作。 总之,通过将作为参数传递给函数或方法,可以实现对数据的访问和操作。无论是使用指针还是引用,都可以实现对进行传递,并在函数或方法中对进行相关操作。这样可以增强程序的灵活性和可扩展性。 ### 回答3: STI库的可以作为参数传递给其他函数或方法。是一种后进先出(Last In First Out,LIFO)的数据结构,用于存储和管理函数的局部变量、参数以及返回地址等信息。 当将STI库的作为参数传递给其他函数时,可以通过传递的指针或引用来实现。通过传递的指针,其他函数可以访问和修改的内容。这样,其他函数就能够利用来存储和操作自己的局部变量,而不会影响到原来的。 通过将STI库的作为参数传递给其他函数,可以实现跨函数的局部变量的共享和传递。这样可以节省内存空间,避免了在函数之间频繁地进行数据复制。此外,还可以实现递归函数的调用,因为递归函数可以直接使用STI库的来保存多层次的函数调用信息。 需要注意的是,在将STI库的作为参数传递给其他函数时,需要保证函数之间使用的方式一致,以避免出现错误。此外,还需要注意的分配和释放的正确性,以防止内存泄漏或错误的访问的内容。 总之,STI库的可以通过传递指针或引用的方式作为参数,实现函数之间的数据共享和传递,并提供了一种有效的方式来管理函数的局部变量和函数调用信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值