7

正如 Sebesta 在《编程语言概念》一书中提到的:

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

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

4

4 回答 4

17

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

此外,问题不在于递归算法难以使用字段,而更普遍的是,重入算法或将在多个线程上调用相同代码的算法难以使用字段。

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

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

于 2012-04-24T04:43:41.387 回答
9

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

于 2012-04-24T01:34:30.233 回答
5

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

鉴于此 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 得名的地方。可以说,程序员都喜欢递归ツ</p>

现场测试:http: //ideone.com/Xl86q

于 2012-04-24T02:42:21.427 回答
2

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

于 2012-04-24T01:35:24.487 回答