Skip to content

Runtime Environment

:material-circle-edit-outline: 约 931 个字 :material-clock-time-two-outline: 预计阅读时间 3 分钟

CP Part.4 Runtime Environment

程序执行时的存储器组织

image-20250407095553160

我们将较小的编译时常量直接插入到代码中,将大的整型值、浮点值、字符串分配到全局/静态区域中的存储器,在启动时仅保存一次;之后再由执行代码从这些位置中得到。

这里的堆 (heap) 是一个简单的线性存储器区域,与数据结构中提到的堆无关。

栈上的内容是符合后进先出的,而堆上的内容不一定。

存储器分配时的一个重要单元是 过程活动记录 (procedure activation record),其用途是保存调用函数时产生的局部数据。其至少应当包含如下几个部分:

image.png

用于 bookkeeping 的空间对于每个函数可能相同;用于保存参数、局部数据、局部临时变量的空间可能因不同的函数而大小不同

不同的语言可能将 activation record 分配在不同的地方

处理器寄存器也是 runtime environment 的结构部分。

大多数体系结构里都有 PC,SP;有的还有 FP (frame pointer, X86 里的 BP) 等。不同的体系结构的处理器的数量和使用差别较大。

几乎所有的程序语言都依赖于如下 3 种 runtime environment 的某一个,其主要结构并不依赖于目标机器的特定细节:

  • FORTRAN77 的完全静态环境
  • C, C++, Pascal, Ada 等的基于栈的环境
  • LISP 等的完全动态环境

这三种类型的混合模式也是可能的。

完全静态环境 | Fully Static Environment

最简单的运行时环境,其所有数据(包括各个函数的 activation record)都是静态的,这种环境不支持动态分配以及函数的递归调用

基于栈的环境 | Stack-Based Environment

Stack of activation record, a.k.a. runtime stack / call stack 随着函数的调用而生长或减小。

没有局部过程的基于栈的环境

先讨论以 C 语言为例的 没有局部函数 的基于栈的环境

我们会用 sp 指向当前栈顶,一个框架指针 (frame pointer, fp; a.k.a 帧指针, bp) 指向当前活动,这两个指针通常保存在寄存器中

先前活动的 fp 会保存在它调用的函数的活动记录中,形成控制链 (control link; a.k.a 动态链, dynamic link)。

image.png

带有局部过程的基于栈的环境

对于 Pascal 之类的语言,局部函数 是被允许的,即允许在局部作用域定义函数

带有过程参数的基于栈的环境

在一些语言里,过程也可以作为参数

完全动态环境 | Fully Dynamic Environment

Parameter Passing Mechanisms | 参数传递机制

值传递 | Pass by Value

就是 C 语言中的参数传递方式,C 语言中即使我们通过传入指针变量实现一些操作,其本质上还是将实参中指针变量的值(即所指地址)复制到形参的相应变量中去

引用传递 | Pass by Reference

传递变量的引用,即传递其在存储空间中的位置。在函数中所做的一切更改都会作用于这个变量本身。

值结果传递 | Pass by Value-Result

将实参从左到右逐个复制到形参中,在函数运行结束后再逐个将其复制回原来的位置。

名字传递 | Pass by Name

名字传递的思想是:直到函数真正使用了某个参数时才对其赋值,因此也称为 延迟赋值 (delayed evaluation)

等价的做法是将函数在调用的原位进行展开。