如何找到变量并在恒定时间内完成

时间:2018-04-30 08:14:56

标签: recursion stack computer-science

我最近想到的一件事是计算机如何找到他的变量。当我们运行程序时,程序将在堆栈中创建多个层,为它打开的每个新范围创建一个层,并在此范围内的堆中存储变量值或指针。范围完成后,它及其所有变量将被销毁。但计算机如何知道其变量在哪里?如果相同的变量更频繁地出现,那么使用哪些变量。

我怎么想象,计算机搜索它所在的范围就像一个数组,如果它没有找到变量,它就像链接列表一样向下跟随堆栈,并像下一个数组一样搜索下一个范围。

这导致假设全局变量使用最慢,因为它必须一直遍历到最后一个范围。因此它具有来自a * n的计算时间(a =每个范围的平均变量数量,n =范围数量)。如果我现在假设我的代码是递归的并且在全局变量的递归函数调用内(让我们说我已经定义了变量const PI = 3.1416并且我在每次递归中使用它),那么它将遍历它每次调用再次向后,如果我的递归函数需要1000次递归,那么它会执行1000次。

但另一方面,在学习递归时,我从未听说过如果可能的话,应该避免引用在递归范围内找不到的变量。所以我想知道我的想法是否正确。有人可以就这个问题说清楚。

1 个答案:

答案 0 :(得分:1)

你反过来说:范围,框架,堆不做变量,变量做范围,框架,堆。
实际上两者都有点延伸,但我的观点是避免关注变量的生命周期(这就像堆和堆栈这样的术语真正意味着什么),而是深入了解一下。

内存是一种存储形式,每个单元格都分配一个数字,单元格称为 word ,数字称为地址
地址集称为地址空间,地址空间通常是地址范围或地址范围的并集。

编译器假定程序数据将加载到特定地址,例如 X ,并且在 X 之后有足够的内存(即 X <所有数据的/ em> +1, X + 2, X +3,...,都存在)。
然后从 X 开始按顺序排列变量,编译器的工作是保持地址 X + k 之间的关联变量实例
请注意,变量可以多次实例化,调用函数两次或递归都是其中的示例 在第一种情况下,两个实例可以共享相同的地址 X + k ,因为它们不会在时间上重叠(到第二个实例存活时,首先结束了。) 在第二种情况下,两个实例在时间上重叠,并且必须使用两个地址。

因此我们看到变量的生命周期会影响变量名称与其地址(变量的分配)之间的映射方式。
两种常见的策略是:

  • 堆栈
    我们从地址 X + b 开始,并在连续的地址分配新实例 X + b +1,< em> X
    + b +2等。
    当前地址(例如 X + b +54)存储在某处(它是堆栈指针)。
    当我们想要释放变量时,我们将堆栈指针设置回来(例如从 X + b +54到 X + b < / EM> 53)。
    我们可以看到,释放不是最后分配的变量是不可能的 这允许非常快速分配/释放,并且自然适合保存局部变量的函数框架的需要:当调用函数时,分配新变量,当它结束时,它们被移除。登记/> 根据我们上面提到的,我们看到如果 f 调用 g (即 f g 的父级)那么 f 的变量不能在 g 之前解除分配。
    这再次自然地符合函数的语义。


  • 此策略动态在地址 X + o 中分配变量实例。
    运行时保留一个地址块并管理它们的状态(空闲,被占用),当被问到时,它可以给出一个空闲地址并标记它被占用。
    例如,这对于分配大小取决于用户输入的对象很有用。

  • 堆(静态)
    有些变量具有程序的生命周期,但它们的大小和数量在编译时是已知的 在这种情况下,编译器只是为每个实例分配一个唯一的地址 X + i 。 它们不能被解除分配,它们与程序代码一起被批量加载到内存中并保持在那里直到程序被卸载。

我留下了一些细节,比如堆栈通常从大地址到较低地址增长(因此它可以放在内存的最边缘),并且变量占用多个地址。 /> 一些编程语言,特别是解释的编程语言,不会将地址与变量实例相关联,而是在变量名称(正确限定)和变量值之间保持映射,这样可以通过许多特定方式控制变量的生命周期(参见Javascript中的Closure)。

全局变量在静态堆中分配,只存在一个实例(只有一个地址) 每个使用它的递归函数总是直接引用唯一的实例,因为在编译时已知唯一的地址 函数中的局部变量在堆栈中分配,函数的每次调用(递归与否)都使用一组新的实例(每次地址不需要相同,但它们可以)。

简单地说,没有查找,变量被分配,以便代码可以在编译器(相对地,在堆栈中,或绝对地,在堆中)访问它们。