调用堆栈是从底部开始还是从顶部开始?


11

堆栈是自底向上堆积的东西。

因此,当调用函数时,调用堆栈会在堆栈上添加新的项目,并且随着每个函数的结束从堆栈中删除项目,直到堆栈为空,然后程序结束。

如果上述正确,那么为什么人们会提到控件将调用堆栈“上移”?控制肯定会向下移动到调用堆栈,直到到达底部为止。


调用函数时,会将项目添加到堆栈的顶部,并将控件传递给该函数。因此,控件从堆栈中的基础项目移动到其顶层项目-向上。
treecoder

1
@greengit:表达式“向上调用堆栈”用于例外情况,其中控制实际上以相反的方式移动。
Michael Borgwardt'5

@MichaelBorgwardt:你是对的。
treecoder

1
@MichaelBorgwardt:我见过的表达“错误动调用栈”。肯定那是不正确的。
CJ7

Answers:


9

这种用法有两个可能的原因:

  • 在例外情况下,控件移至调用函数/方法,并且此调用层次结构通常在主方法位于顶部的情况下可视化,而方法调用向下形成层次结构,并降低抽象级别。在此层次结构中,异常向上移动。

  • 普通x86应用程序中的实际程序堆栈是反转的,即,它向下增长。PUSH / PUSHW / PUSHD机器码指令减少堆栈指针。其他体系结构可以共享此模型。


自上而下的想法与“堆栈”是从底部开始的一堆物品的日常概念相反吗?
CJ7

@CraigJ:堆栈内容每个字节的位也将物理存储在单独的芯片中,这也是事实。谁在乎?
Michael Borgwardt'5

1

一切都取决于单词的定义。在本文中,“顶部”和“底部”分别是什么意思,以及在操作系统或计算机体系结构的实现上是什么意思。

我记得很久以前的事情,当时我在Commodore 64上编程。地址$ 0800(2048)和$ 9FFF(40959)之间的存储器是为BASIC程序保留的。您的BASIC程序的代码存储在较低的地址($ 0800,从该地址开始向上)处存储。该堆栈用于存储变量和子例程的返回地址,该堆栈从该范围的顶部($ 9FFF)开始,然后向较低的地址扩展。因此,在这种情况下,将堆栈向下看是合乎逻辑的,并且当您从子例程返回时,通过增加堆栈指针来丢弃该子例程的堆栈帧,以便您可以说您在“向上移动堆栈”时从子例程返回。

我不知道它如何在Windows或Intel x86处理器等现代版本中工作。也许堆栈以相反的方式工作,即,它从较低的地址增长到较高的地址。如果真是这样,那么您可能恰恰相反地使用了“顶部”,“底部”和“向上”,“向下”这两个词。


0

调用诸如foo(6,x + 1)之类的函数...

  1. 在调用者的上下文中评估实际的参数表达式,例如x + 1。
  2. 通过将适当的内存“本地块”推入专用于此目的的运行时“调用堆栈”,为foo()的本地用户分配内存。对于参数而非局部变量,请将步骤(1)中的值存储到foo()的局部块中的相应插槽中。
  3. 存储调用者的当前执行地址(其“返回地址”),并将执行切换为foo()。
  4. foo()以其本地块执行,可在调用堆栈的末尾方便地使用。
  5. 当foo()完成时,它将通过从堆栈中弹出其本地变量而退出,并使用先前存储的返回地址“返回”到调用者。现在,调用者的本地用户位于堆栈的末尾,它可以继续执行。

参考:

http://cslibrary.stanford.edu/102/PointersAndMemory.pdf(p15


请注意,这高度特定于调用约定。有迹象表明,让调用清理调用约定,大多数使用至少一些寄存器等

0

如果将堆栈概念化为自下而上的事物,例如在正常重力情况下将网球球放入其中的圆柱体,则控件会在调用函数时向上移动堆栈。功能完成后,控件将向下移动堆栈。

如果将堆栈概念化为自上而下的东西,例如同一圆柱网球,但重力相反,则控件会在调用函数时在堆栈中向下移动,并在函数完成时在堆栈中向上移动。

这些都是您脑海中的模型,本质上是完全任意的。如果愿意,可以将其概念化为并列事物,但可能无法与人沟通。我个人认为,如果A调用B且B调用C,则C是堆栈的底部(反重力现实),并且如果C中发生异常,则您想将该异常“冒泡”到A。我认为这可能是更常见的语言用法,因为感觉C是最深层的,而A是最上层的。第一个功能更直观地是我的顶部,并且每个调用的功能都会更深入。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.