为什么静态变量不允许递归?

时间:2012-04-24 01:19:19

标签: c# c recursion

如Sebesta的“编程语言概念”一书所述:

  • 静态变量提供全局访问,可以在子程序调用之间保留值(历史敏感),并且效率很高。
  • 静态变量不支持递归

为什么静态变量不支持递归?这是因为如果发生递归会浪费大量内存,因为它是static并且这意味着在整个程序终止之前它不会从内存中释放出来吗?

4 个答案:

答案 0 :(得分:17)

在递归算法中难以使用静态字段的原因并不是它们是静态,而是它们与激活无关。非静态字段在递归算法中同样难以有效使用。

此外,问题不在于使用带有递归算法的字段很难,但更常见的是,使用可重入算法的字段很难使用,或算法,在多个线程上调用相同的代码。

使局部变量和形式参数在递归和其他重入场景中有用的原因是它们与激活相关联。

简而言之:这本书令人困惑,因为它太具体了。这就像说在白色桌子上平衡一个棕色的鸡蛋是很困难的;这是真的,但棕色和白色与它有什么关系呢?在任何表上很难平衡任何蛋。在递归调用中很难正确使用静态字段,因为很难在任何重入场景中正确使用任何字段。

答案 1 :(得分:8)

每个递归调用都将访问相同的静态变量,通过其他调用覆盖存储在变量中的值。在进行递归时通常不希望这样做,除非静态变量服务于某些纯粹的迭代目的,例如,计算调用函数的次数。

答案 2 :(得分:4)

也许一些示例代码可以说明在使用递归时如何管理内存。如何在递归例程中使用静态变量是一个无法启动(一个可能的例外是当你想要一个全局计数器来计算调用函数的次数时,无论调用该方法的方式如何(例如从多线程或连续调用)呼叫))

鉴于此C代码:

#include <iostream>
using namespace std;


void callMe(int j) {

        static int i = 0;

        ++i;
        ++j;
        printf("Loop: %d\n", i);

        printf("i memory location: %p\n", &i);
        printf("j memory location: %p\n", &j);            
        printf("\n");

        // change i to j ,
        // so the other next method invocations
        // or simultaneous(e.g. multi-threaded) method invocations
        // of this method can work independently from 
        // other method invocations / simultaneous method invocations
        if ( i < 10 ) 
            callMe(j);

        printf("Returning from loop %d\n", j);        
}

int main() {     
        callMe(0);
        printf("Next\n");
        callMe(0);     
        return 0;     
}

输出:

Loop: 1
i memory location: 0x804a038
j memory location: 0xbf8b69c0

Loop: 2
i memory location: 0x804a038
j memory location: 0xbf8b69b0

Loop: 3
i memory location: 0x804a038
j memory location: 0xbf8b69a0

Loop: 4
i memory location: 0x804a038
j memory location: 0xbf8b6990

Loop: 5
i memory location: 0x804a038
j memory location: 0xbf8b6980

Loop: 6
i memory location: 0x804a038
j memory location: 0xbf8b6970

Loop: 7
i memory location: 0x804a038
j memory location: 0xbf8b6960

Loop: 8
i memory location: 0x804a038
j memory location: 0xbf8b6950

Loop: 9
i memory location: 0x804a038
j memory location: 0xbf8b6940

Loop: 10
i memory location: 0x804a038
j memory location: 0xbf8b6930

Returning from loop 10
Returning from loop 9
Returning from loop 8
Returning from loop 7
Returning from loop 6
Returning from loop 5
Returning from loop 4
Returning from loop 3
Returning from loop 2
Returning from loop 1
Next
Loop: 11
i memory location: 0x804a038
j memory location: 0xbf8b69c0

Returning from loop 1

正如我们所看到的,static i变量在内存中只有一个位置;因此,这些值可以在两次调用之间存活。如果您想要分解并克服给定问题,尤其是在多线程方法调用上,这是不可取的。如果在callMe方法上同时进行两次调用,则另一个callMe可能无法完成其任务,因为这些方法调用只是使用相同的变量实例(静态变量),这些方法调用会产生副作用相互之间,这些方法不能相互独立地工作,因为它们没有变量的独立副本。

上面的代码即使它不是多线程的,下一个方法调用也无法完成其任务,因为第二个调用访问相同的变量(静态变量)并接收已被先前方法调用污染的值

简单来说,静态变量即使是在函数内部,仍然是一个全局变量。函数内部的静态变量只是防止名称冲突,但是对于所有意图和目的,静态变量是全局变量

要使代码按预期执行,请将if (i < 10)更改为if (j < 10)

顺便说一句,非静态变量被分配了自己的内存,它被分配在堆栈上。如果我们没有停止条件,在许多递归调用之后,上面的代码将产生堆栈溢出错误,这就是stackoverflow得名的地方。可以说,程序员喜欢recursion

实时测试:http://ideone.com/Xl86q

答案 3 :(得分:2)

递归层对象的多个实例,每个实例保留自己的数据。所有实例共享静态变量。我确定一个静态变量可以被递归过程使用,并且在用作常量时是合适的,但是在动态使用时会使调试变得困难。